diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 1cad7ba3c..888c73f77 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -136,6 +136,7 @@ tenant: - sys_role_menu - sys_user_post - sys_user_role + - sys_message - sys_client - sys_oss_config - ums_member @@ -145,6 +146,7 @@ tenant: - ums_cart - ums_account - ums_account_change_record + - sys_message_template - ums_fans - ums_block - oms_aftersale diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/MemberFill.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/MemberFill.java deleted file mode 100644 index d09db2b61..000000000 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/MemberFill.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.dromara.common.web.core; - -public interface MemberFill { -} diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillAspect.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillAspect.java new file mode 100644 index 000000000..ce295132e --- /dev/null +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillAspect.java @@ -0,0 +1,127 @@ +package com.wzj.soopin.member.annotation; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.wzj.soopin.member.domain.vo.MemberVO; +import com.wzj.soopin.member.service.IMemberService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.dromara.common.core.domain.R; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.Collection; + +@Aspect +@Component +@RequiredArgsConstructor +@Slf4j +public class MemberFillAspect { + + private final IMemberService memberService; + + @Around("@annotation(com.wzj.soopin.member.annotation.MemberFillMethod)") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + Object result = joinPoint.proceed(); + if (result instanceof R) { + Object data = ((R) result).getData(); + if (data != null) { + processObject(data); + } + } + return result; + } + + private void processObject(Object obj) { + + //基础类型跳过 + if (obj == null || isPrimitiveType(obj.getClass())) return; + + // 处理集合类型 + if (obj instanceof Collection) { + for (Object item : (Collection) obj) { + processObject(item); + } + return; + } + + // 处理分页类型 + if (obj instanceof IPage) { + + for (Object item : (Collection)((IPage) obj).getRecords()) { + processObject(item); + } + return; + } + + // 处理数组类型 + if (obj.getClass().isArray()) { + int length = java.lang.reflect.Array.getLength(obj); + for (int i = 0; i < length; i++) { + processObject(java.lang.reflect.Array.get(obj, i)); + } + return; + } + + + + // 同原 MemberFillInterceptor 中的逻辑,简化处理 + Class clazz = obj.getClass(); + while (clazz != null && clazz != Object.class) { + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(MemberFillField.class)) { + handleMemberFill(obj, field); + } + } + clazz = clazz.getSuperclass(); + } + } + + // 判断是否为基本类型或包装类型 + private boolean isPrimitiveType(Class clazz) { + return clazz.isPrimitive() || + clazz.equals(String.class) || + clazz.equals(Integer.class) || + clazz.equals(Long.class) || + clazz.equals(Double.class) || + clazz.equals(Float.class) || + clazz.equals(Boolean.class) || + clazz.equals(Character.class) || + clazz.equals(Short.class) || + clazz.equals(Byte.class); + } + + private void handleMemberFill(Object obj, Field field) { + try { + field.setAccessible(true); + MemberFillField annotation = field.getAnnotation(MemberFillField.class); + String memberIdFieldName = annotation.id(); + + Field idField = findField(obj.getClass(), memberIdFieldName); + idField.setAccessible(true); + Object memberId = idField.get(obj); + + if (memberId instanceof Long) { + MemberVO member = memberService.getMemberInfo((Long) memberId); + if (member != null) { + field.set(obj, member); + } + } + } catch (Exception e) { + log.error("会员信息填充失败", e); + } + } + + private Field findField(Class clazz, String fieldName) throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) throw e; + return findField(superClass, fieldName); + } + } +} + diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillField.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillField.java new file mode 100644 index 000000000..94aa1013c --- /dev/null +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillField.java @@ -0,0 +1,21 @@ +package com.wzj.soopin.member.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义vo处理器,用于标注需要填充member的对象 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MemberFillField { + + /** + * 指向memberId字段名 + */ + String id() default ""; +} diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillMethod.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillMethod.java new file mode 100644 index 000000000..ae1320eed --- /dev/null +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/annotation/MemberFillMethod.java @@ -0,0 +1,14 @@ +package com.wzj.soopin.member.annotation; + +import java.lang.annotation.*; + +/** + * 自定义vo处理器,用于修饰接口,如果需要自动填充vo中的用户信息则需要增加这个注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MemberFillMethod { + +} diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FansController.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FansController.java index 76c08b763..c968d56ab 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FansController.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FansController.java @@ -1,19 +1,17 @@ package com.wzj.soopin.member.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.wzj.soopin.member.annotation.MemberFillMethod; import com.wzj.soopin.member.convert.FansConvert; -import com.wzj.soopin.member.convert.FeedbackConvert; +import com.wzj.soopin.member.convert.MemberBlockConvert; import com.wzj.soopin.member.domain.bo.FansBO; -import com.wzj.soopin.member.domain.bo.FeedbackBO; import com.wzj.soopin.member.domain.po.Fans; -import com.wzj.soopin.member.domain.po.Feedback; import com.wzj.soopin.member.domain.po.Member; import com.wzj.soopin.member.domain.po.MemberBlock; import com.wzj.soopin.member.domain.vo.FansVO; -import com.wzj.soopin.member.domain.vo.FeedbackVO; +import com.wzj.soopin.member.domain.vo.MemberBlockVO; import com.wzj.soopin.member.service.IFansService; import com.wzj.soopin.member.service.IMemberBlockService; import com.wzj.soopin.member.service.IMemberService; @@ -22,7 +20,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.domain.R; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.redis.utils.RedisUtils; @@ -45,28 +42,29 @@ public class FansController { private final IMemberBlockService memberBlockService; + private final MemberBlockConvert memberblockconvert; @Tag(name ="粉丝列表") @PostMapping("/fan/list") - public R> fansList( Long memberId, @RequestBody Page page) { + public R> fansList(@RequestBody FansBO bo, @RequestBody Page page) { LambdaQueryWrapper< Fans> fansQuery = new LambdaQueryWrapper<>(); - fansQuery.eq(Fans::getVloggerId, memberId); + fansQuery.eq(Fans::getVloggerId, bo.getMemberId()); Page fans= service.page(page,fansQuery); return R.ok(convert.toVO( fans)); } @Tag(name ="关注列表") @PostMapping("/follow/list") - public R> followList( Long memberId, @RequestBody Page page) { + public R> followList(@RequestBody FansBO bo, @RequestBody Page page) { LambdaQueryWrapper< Fans> fansQuery = new LambdaQueryWrapper<>(); - fansQuery.eq(Fans::getFanId, memberId); + fansQuery.eq(Fans::getFanId, bo.getMemberId()); Page fans= service.page(page,fansQuery); return R.ok(convert.toVO( fans)); } @Tag(name ="朋友列表") @PostMapping("/friend/list") - public R> friendList( Long memberId, @RequestBody Page page) { + public R> friendList( @RequestBody FansBO bo, @RequestBody Page page) { LambdaQueryWrapper< Fans> fansQuery = new LambdaQueryWrapper<>(); - fansQuery.eq(Fans::getVloggerId, memberId); + fansQuery.eq(Fans::getVloggerId, bo.getMemberId()); fansQuery.eq(Fans::getFriendFlag, 1); Page fans= service.page(page,fansQuery); return R.ok(convert.toVO( fans)); @@ -152,9 +150,12 @@ public class FansController { } @GetMapping("block/list") - public R> queryBlockVloger(@RequestParam Long myId) { + @MemberFillMethod + public R> queryBlockVloger(@RequestParam Long myId) { - return R.ok(memberBlockService.list(new LambdaQueryWrapper().eq(MemberBlock::getMemberId, myId))); + List blockList= memberBlockService.list(new LambdaQueryWrapper().eq(MemberBlock::getMemberId, myId)); + + return R.ok(memberblockconvert.toVO(blockList)); } /** diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FeedbackController.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FeedbackController.java index bb4cf449a..bd5a9f58e 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FeedbackController.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/FeedbackController.java @@ -112,7 +112,7 @@ public class FeedbackController { @Tag(name = ("处理")) @Log(title = "意见反馈", businessType = BusinessType.UPDATE) - @PostMapping("/handle/handle") + @PostMapping("/handle") public R changeStatus(@RequestBody FeedbackBO bo) { service.handle(convert.toPo(bo)); return R.ok(); diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberAddressController.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberAddressController.java index 3db97d2c3..2290df955 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberAddressController.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberAddressController.java @@ -3,6 +3,7 @@ package com.wzj.soopin.member.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.wzj.soopin.member.annotation.MemberFillMethod; import com.wzj.soopin.member.convert.MemberAddressConvert; import com.wzj.soopin.member.domain.bo.MemberAddressBO; import com.wzj.soopin.member.domain.po.MemberAddress; @@ -27,7 +28,7 @@ import java.util.List; */ @Tag(name ="会员收货地址接口列表") @RestController -@RequestMapping("/ums/memberAddress") +@RequestMapping("/ums/member/address") @RequiredArgsConstructor public class MemberAddressController { @@ -37,6 +38,7 @@ public class MemberAddressController { @Tag(name ="查询会员收货地址列表") @PostMapping("/list") + @MemberFillMethod public R list(@RequestBody MemberAddressBO query,@RequestBody Page page) { Page list = service.page(page,query.toWrapper()); return R.ok(convert.toVO(list)); @@ -53,22 +55,22 @@ public class MemberAddressController { @Tag(name ="获取会员收货地址详细信息") @GetMapping(value = "/{id}") - public R getInfo(@PathVariable("id") Long id) { - return R.ok(service.getById(id)); + public R getInfo(@PathVariable("id") Long id) { + return R.ok(convert.toVO(service.getById(id))); } @Tag(name ="新增会员收货地址") @Log(title = "会员收货地址", businessType = BusinessType.INSERT) @PostMapping("/add") - public R add(@RequestBody MemberAddress memberAddress) { - return R.ok(service.save(memberAddress)); + public R add(@RequestBody MemberAddressBO memberAddress) { + return R.ok(service.save(convert.toPo(memberAddress))); } @Tag(name ="修改会员收货地址") @Log(title = "会员收货地址", businessType = BusinessType.UPDATE) @PostMapping("/update") - public R edit(@RequestBody MemberAddress memberAddress) { - service.updateById(memberAddress); + public R edit(@RequestBody MemberAddressBO memberAddress) { + service.updateById(convert.toPo(memberAddress)); return R.ok(null); } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberCartController.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberCartController.java index 3e7fa6605..2058acfc0 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberCartController.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/controller/MemberCartController.java @@ -4,6 +4,7 @@ package com.wzj.soopin.member.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.wzj.soopin.member.annotation.MemberFillMethod; import com.wzj.soopin.member.convert.MemberCartConvert; import com.wzj.soopin.member.domain.bo.MemberCartBO; import com.wzj.soopin.member.domain.po.MemberCart; @@ -34,6 +35,7 @@ public class MemberCartController extends BaseController { @Tag(name = "查询购物车列表") @PostMapping("/list") + @MemberFillMethod public R> list(@RequestBody MemberCartBO bo, @RequestBody Page page) { Page userPage = service.page(page, bo.toWrapper()); return R.ok(convert.toVO(userPage)); @@ -51,6 +53,7 @@ public class MemberCartController extends BaseController { @Tag(name = "获取购物车详细信息") @GetMapping(value = "/{id}") + @MemberFillMethod public R getInfo(@PathVariable("id") Long id) { return R.ok(convert.toVO(service.getById(id))); } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/convert/MemberBlockConvert.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/convert/MemberBlockConvert.java new file mode 100644 index 000000000..bb05533c2 --- /dev/null +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/convert/MemberBlockConvert.java @@ -0,0 +1,20 @@ +package com.wzj.soopin.member.convert; + +import com.wzj.soopin.member.domain.bo.FeedbackBO; +import com.wzj.soopin.member.domain.po.Feedback; +import com.wzj.soopin.member.domain.po.MemberBlock; +import com.wzj.soopin.member.domain.vo.FeedbackVO; +import com.wzj.soopin.member.domain.vo.MemberBlockVO; +import org.dromara.common.web.core.BaseConverter; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * 意见反馈 DO <=> VO / BO + * + * @author zcc + */ +@Mapper(componentModel = "spring",uses = BaseConverter.class) +public interface MemberBlockConvert extends BaseConverter { + +} diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/FansBO.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/FansBO.java index a7c61d9e8..154bc85c6 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/FansBO.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/FansBO.java @@ -19,6 +19,14 @@ import org.springframework.boot.actuate.integration.IntegrationGraphEndpoint; public class FansBO extends BaseBO { + /** + * 用户 + */ + @Schema(description ="用户id") + private String memberId; + + + /** * 博主id */ diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/MemberAddressBO.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/MemberAddressBO.java index 7165c722c..806e21735 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/MemberAddressBO.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/bo/MemberAddressBO.java @@ -1,7 +1,6 @@ package com.wzj.soopin.member.domain.bo; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.wzj.soopin.member.domain.po.MemberAddress; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -14,12 +13,16 @@ import org.dromara.common.core.domain.BaseBO; */ @Schema(description="会员收货地址 查询 对象") @Data -public class MemberAddressBO extends BaseBO { +public class MemberAddressBO extends BaseBO { + + + + private Long id; @Schema(description ="MEMBER_ID 精确匹配") private Long memberId; @Schema(description ="收货人名称 精确匹配") - private String nameLike; + private String name; @Schema(description ="PHONE 精确匹配") private String phone; @@ -43,10 +46,19 @@ public class MemberAddressBO extends BaseBO { private String detailAddress; @Schema(description ="是否默认 精确匹配") - private Integer isDefault; + private Integer defaultFlag; @Override public LambdaQueryWrapper toWrapper() { - return new QueryWrapper().lambda().eq(MemberAddress::getMemberId,this.getMemberId()); + return super.toWrapper().eq(city!=null,MemberAddress::getCity,city) + .eq(district!=null,MemberAddress::getDistrict,district) + .like(detailAddress!=null,MemberAddress::getDetailAddress,detailAddress) + .eq(phone!=null,MemberAddress::getPhoneEncrypted,phone) + .eq(province!=null,MemberAddress::getProvince,province) + .eq(city!=null,MemberAddress::getCity,city) + .eq(defaultFlag!=null,MemberAddress::getDefaultFlag,defaultFlag) + .eq(memberId!=null,MemberAddress::getMemberId,memberId) + .like(name!=null,MemberAddress::getName,name); + } } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/po/MemberAddress.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/po/MemberAddress.java index 3101e5eef..300806831 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/po/MemberAddress.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/po/MemberAddress.java @@ -2,6 +2,7 @@ package com.wzj.soopin.member.domain.po; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; import lombok.Data; import org.dromara.common.core.domain.model.BaseAudit; import org.dromara.common.excel.annotation.Excel; @@ -15,7 +16,6 @@ import org.dromara.common.excel.annotation.Excel; @Data @TableName("ums_member_address") public class MemberAddress extends BaseAudit { - private static final long serialVersionUID = 1L; @Schema(description ="ID") private Long id; @@ -24,6 +24,7 @@ public class MemberAddress extends BaseAudit { @Excel(name = "MEMBER_ID") private Long memberId; + @Schema(description ="收货人名称") @Excel(name = "收货人名称") private String name; diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/FansVO.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/FansVO.java index 47f7bff2d..c27f13d62 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/FansVO.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/FansVO.java @@ -1,5 +1,6 @@ package com.wzj.soopin.member.domain.vo; +import com.wzj.soopin.member.annotation.MemberFillField; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; @@ -17,6 +18,7 @@ public class FansVO { @Schema(description ="博主id") @Excel(name = "博主id") + @MemberFillField(id = "vloggerId") private MemberVO vlogger; @Schema(description ="博主id") @@ -24,6 +26,7 @@ public class FansVO { @Schema(description ="粉丝id") @Excel(name = "粉丝id") + @MemberFillField(id = "fanId") private MemberVO fan; @Schema(description ="粉丝id") diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberAddressVO.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberAddressVO.java index 41e0ce79b..9bc4a039b 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberAddressVO.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberAddressVO.java @@ -1,5 +1,7 @@ package com.wzj.soopin.member.domain.vo; +import com.wzj.soopin.member.annotation.MemberFillField; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.dromara.common.excel.annotation.Excel; import org.dromara.common.mybatis.core.domain.BaseEntity; @@ -16,6 +18,14 @@ public class MemberAddressVO extends BaseEntity { /** MEMBER_ID */ @Excel(name = "MEMBER_ID") private Long memberId; + + /** MEMBER_ID */ + @Excel(name = "MEMBER_ID") + @Schema(description = "MEMBER_ID") + @MemberFillField(id = "memberId") + private MemberVO member; + + /** 收货人名称 */ @Excel(name = "收货人名称") private String name; @@ -26,7 +36,7 @@ public class MemberAddressVO extends BaseEntity { private String phoneHidden; /** 是否为默认 */ @Excel(name = "是否为默认") - private Integer defaultStatus; + private Integer defaultFlag; /** 邮政编码 */ @Excel(name = "邮政编码") private String postCode; @@ -42,7 +52,5 @@ public class MemberAddressVO extends BaseEntity { /** 详细地址(街道) */ @Excel(name = "详细地址(街道)") private String detailAddress; - /** 是否默认 */ - @Excel(name = "是否默认") - private Integer isDefault; + } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberBlockVO.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberBlockVO.java index e8dff1a0e..df93b7a82 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberBlockVO.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberBlockVO.java @@ -1,7 +1,7 @@ package com.wzj.soopin.member.domain.vo; import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; +import com.wzj.soopin.member.annotation.MemberFillField; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.dromara.common.core.domain.model.BaseAudit; @@ -20,8 +20,15 @@ public class MemberBlockVO extends BaseAudit { @Schema(description ="用户id") private Long memberId; + @Schema(description ="用户") + @MemberFillField(id = "memberId") + private MemberVO member; @Schema(description ="被拉黑人id") private Long blockMemberId; + @Schema(description ="被拉黑人") + @MemberFillField(id = "blockMemberId") + private MemberVO blockMember; + } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberCartVO.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberCartVO.java index c4128e2c4..4ebbbdd36 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberCartVO.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/domain/vo/MemberCartVO.java @@ -1,5 +1,6 @@ package com.wzj.soopin.member.domain.vo; +import com.wzj.soopin.member.annotation.MemberFillField; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.dromara.common.excel.annotation.Excel; @@ -35,6 +36,7 @@ public class MemberCartVO { @Schema(description ="会员") @Excel(name = "会员") + @MemberFillField(id = "memberId") private MemberVO member; diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/IMemberService.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/IMemberService.java index be95e9fa1..28c630e1a 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/IMemberService.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/IMemberService.java @@ -7,6 +7,7 @@ import com.wzj.soopin.member.domain.po.Member; import com.wzj.soopin.member.domain.vo.MemberVO; import org.dromara.common.core.constant.ResultCode; +import java.io.Serializable; import java.util.List; public interface IMemberService extends IService { @@ -60,4 +61,7 @@ public interface IMemberService extends IService { String getPhoneDecrypted(String phoneEncrypted) ; + + + MemberVO getMemberInfo(Serializable id); } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberAddressServiceImpl.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberAddressServiceImpl.java index 6ce679fe6..e2103cc0f 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberAddressServiceImpl.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberAddressServiceImpl.java @@ -41,7 +41,7 @@ public class MemberAddressServiceImpl extends ServiceImpl selectList(MemberAddressBO query, IPage page) { QueryWrapper qw = new QueryWrapper<>(); - String nameLike = query.getNameLike(); + String nameLike = query.getName(); if (!StringUtils.isEmpty(nameLike)) { qw.like("name", nameLike); } diff --git a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberServiceImpl.java b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberServiceImpl.java index 06a02967a..032c206ec 100644 --- a/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberServiceImpl.java +++ b/ruoyi-modules/ruoyi-member/src/main/java/com/wzj/soopin/member/service/impl/MemberServiceImpl.java @@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.wzj.soopin.member.convert.MemberConvert; import com.wzj.soopin.member.domain.bo.MemberBO; import com.wzj.soopin.member.domain.form.ChangeMemberStatusForm; import com.wzj.soopin.member.domain.po.Member; import com.wzj.soopin.member.domain.po.MemberAccount; +import com.wzj.soopin.member.domain.vo.MemberVO; import com.wzj.soopin.member.mapper.MemberAccountMapper; import com.wzj.soopin.member.mapper.MemberCartMapper; import com.wzj.soopin.member.mapper.MemberMapper; @@ -54,6 +56,9 @@ public class MemberServiceImpl extends ServiceImpl implemen private final MemberAccountMapper memberAccountMapper; + + private final MemberConvert memberConvert; + @Override public boolean usernameExists(String username) { return baseMapper.countByUsername(username) > 0; // 确保实现 @@ -102,8 +107,6 @@ public class MemberServiceImpl extends ServiceImpl implemen - @CachePut(value = CacheConstants.MEMBER, key = "#id") - public Integer changeStatus(MemberBO bo) { UpdateWrapper wrapper = new UpdateWrapper<>(); wrapper.eq("id", bo.getId()); @@ -252,4 +255,10 @@ public class MemberServiceImpl extends ServiceImpl implemen public Member getById(Serializable id) { return super.getById(id); } + + + @Override + public MemberVO getMemberInfo(Serializable id) { + return memberConvert.toVO(getById(id)); + } } diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml index 7fdea9248..dd960febd 100644 --- a/ruoyi-modules/ruoyi-system/pom.xml +++ b/ruoyi-modules/ruoyi-system/pom.xml @@ -20,6 +20,7 @@ org.dromara ruoyi-common-core + ${revision} @@ -112,6 +113,23 @@ fastjson + + + org.springframework.boot + spring-boot-starter-websocket + + + + + jakarta.websocket + jakarta.websocket-api + + + + + org.apache.tomcat.embed + tomcat-embed-websocket + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysMessageController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysMessageController.java new file mode 100644 index 000000000..196a8f4a7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysMessageController.java @@ -0,0 +1,183 @@ +package org.dromara.system.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.service.UserService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.core.validate.QueryGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.bo.SysMessageBo; +import org.dromara.system.domain.bo.SysUserBo; +import org.dromara.system.domain.vo.SysMessageVo; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysMessageService; +import org.dromara.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 消息管理 + * + * @author ruoyi + */ +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/message") +public class SysMessageController extends BaseController { + + private final ISysMessageService messageService; + private final ISysUserService userService; + + /** + * 获取当前用户ID + */ + private Long getUserId() { + return LoginHelper.getUserId(); + } + + /** + * 查询消息列表 + */ + @SaCheckPermission("system:message:list") + @Tag(name = "查询消息列表") + @PostMapping("/list") + public R> list(@RequestBody SysMessageBo bo, @RequestBody Page page) { + Page messagePage = messageService.page(page, bo.toWrapper()); + Page voPage = new Page<>(messagePage.getCurrent(), messagePage.getSize(), messagePage.getTotal()); + voPage.setRecords(MapstructUtils.convert(messagePage.getRecords(), SysMessageVo.class)); + return R.ok(voPage); + } + + /** + * 获取消息详细信息 + * + * @param id 消息ID + */ + @SaCheckPermission("system:message:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "消息ID不能为空") @PathVariable Long id) { + return R.ok(messageService.selectMessageById(id)); + } + + /** + * 发送消息 + */ + @SaCheckPermission("system:message:send") + @Log(title = "消息管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/send") + public R send(@Validated(AddGroup.class) @RequestBody SysMessageBo bo) { + List users = userService.selectUserListByDept(null); + List userIds; + + switch (bo.getSendScope()) { + case "all": + // 全部用户 + userIds = users.stream().map(SysUserVo::getUserId).toList(); + break; + case "expert": + // 达人 + userIds = users.stream() + .filter(user -> "expert".equals(user.getUserType())) + .map(SysUserVo::getUserId) + .toList(); + break; + case "merchant": + // 商户 + userIds = users.stream() + .filter(user -> "merchant".equals(user.getUserType())) + .map(SysUserVo::getUserId) + .toList(); + break; + case "user": + // 普通用户 + userIds = users.stream() + .filter(user -> "user".equals(user.getUserType())) + .map(SysUserVo::getUserId) + .toList(); + break; + default: + // 其他情况(如指定userIds) + userIds = bo.getUserIds(); + } + + return toAjax(messageService.sendMessageToUsers(bo, userIds)); + } + + /** + * 标记消息为已读 + */ + @SaCheckPermission("system:message:mark") + @Log(title = "消息管理", businessType = BusinessType.UPDATE) + @PutMapping("/mark/{id}") + public R markAsRead(@NotNull(message = "消息ID不能为空") @PathVariable Long id) { + return toAjax(messageService.markAsRead(id, getUserId())); + } + + /** + * 删除消息 + * + * @param ids 消息ID串 + */ + @SaCheckPermission("system:message:remove") + @Log(title = "消息管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "消息ID不能为空") @PathVariable Long[] ids) { + return toAjax(messageService.deleteMessageByIds(ids)); + } + + /** + * 获取未读消息列表 + */ + @SaCheckPermission("system:message:list") + @Tag(name = "查询未读消息列表") + @PostMapping("/unread") + public R> unreadList(@RequestBody Page page) { + Page unreadPage = messageService.selectUnreadMessagesPage(getUserId(), page); + return R.ok(unreadPage); + } + + /** + * 获取已读消息列表 + */ + @SaCheckPermission("system:message:list") + @Tag(name = "查询已读消息列表") + @PostMapping("/read") + public R> readList(@RequestBody Page page) { + Page readPage = messageService.selectReadMessagesPage(getUserId(), page); + return R.ok(readPage); + } + + /** + * 获取用户列表 + */ +// @SaCheckPermission("system:message:list") +// @GetMapping("/user/list") +// public R> getUserList() { +// PageQuery pageQuery = new PageQuery(); +// pageQuery.setPageNum(1); +// pageQuery.setPageSize(Integer.MAX_VALUE); +// TableDataInfo users = userService.selectPageUserList(new SysUserBo(), pageQuery); +// return R.ok(users); +// } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysMessageTemplateController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysMessageTemplateController.java new file mode 100644 index 000000000..05dbea391 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysMessageTemplateController.java @@ -0,0 +1,97 @@ +package org.dromara.system.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.web.core.BaseController; +import org.dromara.system.domain.SysMessageTemplate; +import org.dromara.system.domain.bo.SysMessageTemplateBo; +import org.dromara.system.domain.vo.SysMessageTemplateVo; +import org.dromara.system.service.ISysMessageTemplateService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 消息模板管理 + * + * @author ruoyi + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/message/template") +public class SysMessageTemplateController extends BaseController { + + private final ISysMessageTemplateService templateService; + + /** + * 查询消息模板列表 + */ + @SaCheckPermission("system:message:template:list") + @Tag(name = "查询消息模板列表") + @PostMapping("/list") + public R> list(@RequestBody SysMessageTemplateBo bo, @RequestBody Page page) { + Page templatePage = templateService.page(page, bo.toWrapper()); + Page voPage = new Page<>(templatePage.getCurrent(), templatePage.getSize(), templatePage.getTotal()); + voPage.setRecords(MapstructUtils.convert(templatePage.getRecords(), SysMessageTemplateVo.class)); + return R.ok(voPage); + } + + /** + * 获取消息模板详细信息 + * + * @param id 消息模板ID + */ + @SaCheckPermission("system:message:template:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "消息模板ID不能为空") @PathVariable Long id) { + SysMessageTemplate template = templateService.getById(id); + return R.ok(MapstructUtils.convert(template, SysMessageTemplateVo.class)); + } + + /** + * 新增消息模板 + */ + @SaCheckPermission("system:message:template:add") + @Log(title = "消息模板管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysMessageTemplateBo bo) { + return toAjax(templateService.save(bo.toEntity())); + } + + /** + * 修改消息模板 + */ + @SaCheckPermission("system:message:template:edit") + @Log(title = "消息模板管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysMessageTemplateBo bo) { + return toAjax(templateService.updateById(bo.toEntity())); + } + + /** + * 删除消息模板 + * + * @param ids 消息模板ID串 + */ + @SaCheckPermission("system:message:template:remove") + @Log(title = "消息模板管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "消息模板ID不能为空") @PathVariable Long[] ids) { + return toAjax(templateService.removeByIds(List.of(ids))); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/converter/SysMessageConvert.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/converter/SysMessageConvert.java new file mode 100644 index 000000000..11fe544f8 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/converter/SysMessageConvert.java @@ -0,0 +1,39 @@ +package org.dromara.system.converter; + +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.vo.SysMessageVo; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 消息对象转换器 + * + * @author ruoyi + */ +@Mapper(componentModel = "spring") +public interface SysMessageConvert { + + SysMessageConvert INSTANCE = Mappers.getMapper(SysMessageConvert.class); + + /** + * SysMessage转SysMessageVo + */ + SysMessageVo convert(SysMessage sysMessage); + + /** + * SysMessage列表转SysMessageVo列表 + */ + List convertList(List sysMessageList); + + /** + * SysMessageVo转SysMessage + */ + SysMessage convert(SysMessageVo sysMessageVo); + + /** + * SysMessageVo列表转SysMessage列表 + */ + List convertListVoToEntity(List sysMessageVoList); +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessage.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessage.java new file mode 100644 index 000000000..8defcfae9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessage.java @@ -0,0 +1,50 @@ +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.domain.model.BaseAudit; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serializable; +import java.util.Date; + +/** + * 消息对象 sys_message + * + * @author ruoyi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_message") +public class SysMessage extends BaseAudit { + + private static final long serialVersionUID = 1L; + + /** 主键ID */ + @TableId(value = "id") + private Long id; + + /** 消息标题 */ + private String title; + + /** 消息内容 */ + private String content; + + /** 消息类型(AUTO自动/MANUAL手动) */ + private String msgType; + + /** 触发条件 */ + private String subType; + + /** 发送者ID */ + private Long senderId; + + /** 定时发送时间 */ + private Date scheduledTime; + +// /** 状态(0正常 1停用) */ +// private String status; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessageTemplate.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessageTemplate.java new file mode 100644 index 000000000..f23165a14 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessageTemplate.java @@ -0,0 +1,65 @@ +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.domain.model.BaseAudit; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.tenant.core.TenantEntity; + +/** + * 消息模板对象 sys_message_template + * + * @author ruoyi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_message_template") +public class SysMessageTemplate extends BaseAudit { + + private static final long serialVersionUID = 1L; + + /** + * 模板ID + */ + @TableId(value = "id") + private Long id; + + /** + * 模板类型(0通知 1公告) + */ + private String templateType; + private String title; + + /** + * 模板编码 + */ + private String templateCode; + + /** + * 模板名称 + */ + private String templateName; + + + /** + * 模板内容 + */ + private String templateContent; + +// /** +// * 模板参数 +// */ +// private String templateParams; + + /** + * 状态(0正常 1停用) + */ + private String status; +// +// /** +// * 备注 +// */ +// private String remark; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessageUser.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessageUser.java new file mode 100644 index 000000000..bfe2cd3ff --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessageUser.java @@ -0,0 +1,50 @@ +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.domain.model.BaseAudit; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.tenant.core.TenantEntity; + +import java.util.Date; + +/** + * 消息用户关联对象 sys_message_user + * + * @author ruoyi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_message_user") +public class SysMessageUser extends BaseAudit { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 消息ID + */ + private Long messageId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 是否已读(0未读 1已读) + */ + private Boolean isRead; + + /** + * 阅读时间 + */ + private Date readTime; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMessageBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMessageBo.java new file mode 100644 index 000000000..e85ae3705 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMessageBo.java @@ -0,0 +1,118 @@ +package org.dromara.system.domain.bo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.domain.model.BaseAudit; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.system.domain.SysMessage; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import io.github.linpeilie.annotations.AutoMapper; + +import java.util.Date; +import java.util.List; + +/** + * 消息业务对象 + * + * @author ruoyi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ExcelIgnoreUnannotated +@AutoMapper(target = SysMessage.class, reverseConvertGenerate = false) +public class SysMessageBo extends BaseAudit { + + /** 主键ID */ + @ExcelProperty(value = "消息ID") + private Long id; + + /** 消息标题 */ + @NotBlank(message = "消息标题不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "消息标题长度不能超过100个字符") + @ExcelProperty(value = "消息标题") + private String title; + + /** 消息内容 */ + @NotBlank(message = "消息内容不能为空", groups = { AddGroup.class, EditGroup.class }) + @ExcelProperty(value = "消息内容") + private String content; + + /** 消息类型(AUTO自动/MANUAL手动) */ + @NotBlank(message = "消息类型不能为空", groups = { AddGroup.class, EditGroup.class }) + @ExcelProperty(value = "消息类型") + private String msgType; + + /** 触发条件 */ + @ExcelProperty(value = "触发条件") + private String subType; + + /** 发送者ID */ + @ExcelProperty(value = "发送者ID") + private Long senderId; + + /** 定时发送时间 */ + @ExcelProperty(value = "定时发送时间") + private Date scheduledTime; + + /** 发送范围(all:全部用户, userType:按用户类型, userIds:指定用户) */ + @NotBlank(message = "发送范围不能为空", groups = { AddGroup.class }) + private String sendScope; + + /** 接收用户ID列表 */ + private List userIds; + + /** 是否发送给所有用户 */ + private Boolean sendToAll; + + /** 扩展数据(JSON格式) */ + private String extraData; + + /** 状态(0正常 1停用) */ + private String status; + + /** 备注 */ + private String remark; + + /** + * 转换为查询条件 + */ + public LambdaQueryWrapper toWrapper() { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.like(StringUtils.isNotBlank(this.getTitle()), SysMessage::getTitle, this.getTitle()) + .eq(StringUtils.isNotBlank(this.getMsgType()), SysMessage::getMsgType, this.getMsgType()) + .eq(StringUtils.isNotBlank(this.getSubType()), SysMessage::getSubType, this.getSubType()) + .eq(this.getSenderId() != null, SysMessage::getSenderId, this.getSenderId()) +// .eq(StringUtils.isNotBlank(this.getStatus()), SysMessage::getStatus, this.getStatus()) + .orderByDesc(SysMessage::getCreateTime); + return lqw; + } + + /** + * 转换为实体对象 + */ + public SysMessage toEntity() { + SysMessage entity = new SysMessage(); + entity.setId(this.getId()); + entity.setTitle(this.getTitle()); + entity.setContent(this.getContent()); + entity.setMsgType(this.getMsgType()); + entity.setSubType(this.getSubType()); + entity.setSenderId(this.getSenderId()); + entity.setScheduledTime(this.getScheduledTime()); +// entity.setExtraData(this.getExtraData()); +// entity.setStatus(this.getStatus()); +// entity.setRemark(this.getRemark()); + entity.setCreateBy(this.getCreateBy()); + entity.setCreateTime(this.getCreateTime()); + entity.setUpdateBy(this.getUpdateBy()); + entity.setUpdateTime(this.getUpdateTime()); + return entity; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMessageTemplateBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMessageTemplateBo.java new file mode 100644 index 000000000..398ff67f6 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMessageTemplateBo.java @@ -0,0 +1,104 @@ +package org.dromara.system.domain.bo; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.dromara.common.core.domain.model.BaseAudit; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.system.domain.SysMessageTemplate; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** + * 消息模板业务对象 + * + * @author ruoyi + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysMessageTemplate.class, reverseConvertGenerate = false) +public class SysMessageTemplateBo extends BaseAudit { + + /** + * 模板ID + */ + @NotNull(message = "模板ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 模板名称 + */ + @NotBlank(message = "模板名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(max = 100, message = "模板名称长度不能超过{max}个字符") + private String templateName; + + /** + * 模板编码 + */ + @NotBlank(message = "模板编码不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(max = 50, message = "模板编码长度不能超过{max}个字符") + private String templateCode; + + /** + * 模板类型(SMS=短信 MAIL=邮件 WECHAT=微信 SYSTEM=系统消息) + */ + @NotBlank(message = "模板类型不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(max = 20, message = "模板类型长度不能超过{max}个字符") + private String templateType; + + /** + * 模板内容 + */ + @NotBlank(message = "模板内容不能为空", groups = { AddGroup.class, EditGroup.class }) + private String templateContent; + + /** + * 状态(0正常 1停用) + */ + private String status; + /** + * 标题 + */ + private String title; +// /** +// * 备注 +// */ +// private String remark; + + /** + * 将当前对象转换为查询条件 + */ + public LambdaQueryWrapper toWrapper() { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.like(StringUtils.isNotBlank(templateName), SysMessageTemplate::getTemplateName, templateName) + .like(StringUtils.isNotBlank(templateCode), SysMessageTemplate::getTemplateCode, templateCode) + .eq(StringUtils.isNotBlank(templateType), SysMessageTemplate::getTemplateType, templateType) + .eq(StringUtils.isNotBlank(status), SysMessageTemplate::getStatus, status) + .orderByDesc(SysMessageTemplate::getCreateTime); + return lqw; + } + + /** + * 转换为实体对象 + */ + public SysMessageTemplate toEntity() { + SysMessageTemplate entity = new SysMessageTemplate(); + entity.setId(id); + entity.setTemplateName(templateName); + entity.setTemplateCode(templateCode); + entity.setTemplateType(templateType); + entity.setTemplateContent(templateContent); + entity.setStatus(status); + entity.setTitle(title); +// entity.setRemark(remark); + return entity; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/event/MessageEvent.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/event/MessageEvent.java new file mode 100644 index 000000000..9cea99281 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/event/MessageEvent.java @@ -0,0 +1,23 @@ +package org.dromara.system.domain.event; + +import lombok.Getter; +import org.dromara.system.domain.vo.SysMessageVo; +import org.springframework.context.ApplicationEvent; + +/** + * 消息事件 + * + * @author ruoyi + */ +@Getter +public class MessageEvent extends ApplicationEvent { + + private final SysMessageVo message; + private final Long userId; + + public MessageEvent(Object source, SysMessageVo message, Long userId) { + super(source); + this.message = message; + this.userId = userId; + } +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageTemplateVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageTemplateVo.java new file mode 100644 index 000000000..10bb840a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageTemplateVo.java @@ -0,0 +1,62 @@ +package org.dromara.system.domain.vo; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.SysMessageTemplate; + +import java.io.Serializable; + +/** + * 消息模板视图对象 sys_message_template + * + * @author ruoyi + */ +@Data +@AutoMapper(target = SysMessageTemplate.class) +public class SysMessageTemplateVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 模板ID + */ + private Long id; + + /** + * 模板类型(1通知 2公告 3消息) + */ + private String templateType; + + /** + * 模板编码 + */ + private String templateCode; + + /** + * 模板名称 + */ + private String templateName; + + /** + * 模板内容 + */ + private String templateContent; + +// /** +// * 模板参数 +// */ +// private String templateParams; + + /** + * 状态(0正常 1停用) + */ + private String status; + +// /** +// * 备注 +// */ +// private String remark; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageUserVo.java new file mode 100644 index 000000000..635c1beb1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageUserVo.java @@ -0,0 +1,45 @@ +package org.dromara.system.domain.vo; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serializable; +import java.util.Date; + +/** + * 消息用户关联视图对象 sys_message_user + * + * @author ruoyi + */ +@Data +//@EqualsAndHashCode(callSuper = true) +public class SysMessageUserVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + private Long id; + + /** + * 消息ID + */ + private Long messageId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 是否已读(0未读 1已读) + */ + private Boolean isRead; + + /** + * 阅读时间 + */ + private Date readTime; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageVo.java new file mode 100644 index 000000000..455a29e47 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageVo.java @@ -0,0 +1,65 @@ +package org.dromara.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.SysNotice; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 消息视图对象 + * + * @author ruoyi + */ +@Data +@AutoMapper(target = SysMessage.class) +public class SysMessageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 主键ID */ + @ExcelProperty(value = "消息ID") + private Long id; + + /** 消息标题 */ + @ExcelProperty(value = "消息标题") + private String title; + + /** 消息内容 */ + @ExcelProperty(value = "消息内容") + private String content; + + /** 消息类型(AUTO自动/MANUAL手动) */ + @ExcelProperty(value = "消息类型") + private String msgType; + + /** 触发条件 */ + @ExcelProperty(value = "触发条件") + private String subType; + + /** 发送者ID */ + @ExcelProperty(value = "发送者ID") + private Long senderId; + + /** 发送者名称 */ + @ExcelProperty(value = "发送者") + private String senderName; + + /** 定时发送时间 */ + @ExcelProperty(value = "定时发送时间") + private Date scheduledTime; + +// /** 状态(0正常 1停用) */ +// @ExcelProperty(value = "状态") +// private String status; + + /** 创建时间 */ + @ExcelProperty(value = "创建时间") + private Date createTime; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/MessageEventListener.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/MessageEventListener.java new file mode 100644 index 000000000..0c38d1c0e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/MessageEventListener.java @@ -0,0 +1,35 @@ +package org.dromara.system.event; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.system.domain.event.MessageEvent; +import org.dromara.system.websocket.MessageWebSocketServer; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * 消息事件监听器 + * + * @author ruoyi + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class MessageEventListener { + + private final MessageWebSocketServer messageWebSocketServer; + + /** + * 处理消息事件 + */ + @Async + @EventListener + public void handleMessageEvent(MessageEvent event) { + try { + messageWebSocketServer.sendMessage(event.getUserId(), String.valueOf(event.getMessage())); + } catch (Exception e) { + log.error("处理消息事件失败", e); + } + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageMapper.java new file mode 100644 index 000000000..03bcd9c77 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageMapper.java @@ -0,0 +1,23 @@ +package org.dromara.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.vo.SysMessageVo; + +/** + * 消息Mapper接口 + * + * @author ruoyi + */ +public interface SysMessageMapper extends BaseMapper { + + /** + * 查询用户未读消息分页列表 + * + * @param page 分页参数 + * @param userId 用户ID + * @return 消息分页列表 + */ + Page selectPageUnreadMessages(Page page, Long userId); +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageTemplateMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageTemplateMapper.java new file mode 100644 index 000000000..7220814e7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageTemplateMapper.java @@ -0,0 +1,14 @@ +package org.dromara.system.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.system.domain.SysMessageTemplate; +import org.dromara.system.domain.vo.SysMessageTemplateVo; + +/** + * 消息模板Mapper接口 + * + * @author ruoyi + */ +public interface SysMessageTemplateMapper extends BaseMapperPlus { + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageUserMapper.java new file mode 100644 index 000000000..13730aa4b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageUserMapper.java @@ -0,0 +1,32 @@ +package org.dromara.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.system.domain.SysMessageUser; +import org.dromara.system.domain.vo.SysMessageUserVo; + +/** + * 消息用户关联Mapper接口 + * + * @author ruoyi + */ +public interface SysMessageUserMapper extends BaseMapperPlus { + + /** + * 更新消息读取状态 + * + * @param messageId 消息ID + * @param userId 用户ID + * @param isRead 是否已读 + * @return 结果 + */ + default int updateReadStatus(Long messageId, Long userId, boolean isRead) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysMessageUser::getMessageId, messageId) + .eq(SysMessageUser::getUserId, userId); + SysMessageUser messageUser = new SysMessageUser(); + messageUser.setIsRead(isRead); + return update(messageUser, lqw); + } +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java index 46695aa4c..99a25cb33 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java @@ -120,4 +120,19 @@ public interface SysUserMapper extends BaseMapperPlus { }) int updateById(@Param(Constants.ENTITY) SysUser user); + /** + * 根据用户类型查询用户ID列表 + * + * @param userType 用户类型 + * @return 用户ID列表 + */ + List selectUserIdsByType(@Param("userType") String userType); + + /** + * 查询所有用户ID + * + * @return 用户ID列表 + */ + List selectAllUserIds(); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IMessageWebSocketService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IMessageWebSocketService.java new file mode 100644 index 000000000..ce321e34d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IMessageWebSocketService.java @@ -0,0 +1,26 @@ +package org.dromara.system.service; + +import org.dromara.system.domain.vo.SysMessageVo; + +/** + * WebSocket消息服务接口 + * + * @author ruoyi + */ +public interface IMessageWebSocketService { + + /** + * 发送消息给指定用户 + * + * @param userId 用户ID + * @param message 消息内容 + */ + void sendMessage(Long userId, SysMessageVo message); + + /** + * 广播消息给所有在线用户 + * + * @param message 消息内容 + */ + void broadcastMessage(SysMessageVo message); +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageService.java new file mode 100644 index 000000000..0fb33c2e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageService.java @@ -0,0 +1,147 @@ +package org.dromara.system.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.bo.SysMessageBo; +import org.dromara.system.domain.vo.SysMessageVo; + +import java.util.List; + +/** + * 消息Service接口 + * + * @author ruoyi + */ +public interface ISysMessageService extends IService { + + /** + * 分页查询消息列表 + */ + TableDataInfo selectPageMessageList(SysMessageBo bo, PageQuery pageQuery); + + /** + * 查询未读消息分页(Page方式) + */ + Page selectUnreadMessagesPage(Long userId, Page page); + + /** + * 查询已读消息分页(Page方式) + */ + Page selectReadMessagesPage(Long userId, Page page); + + /** + * 发送消息给指定用户 + * + * @param message 消息内容 + * @param userId 用户ID + * @return 结果 + */ + int sendMessageToUser(SysMessageBo message, Long userId); + + /** + * 发送消息给多个用户 + * + * @param message 消息内容 + * @param userIds 用户ID列表 + * @return 结果 + */ + int sendMessageToUsers(SysMessageBo message, List userIds); + + /** + * 发送自动消息 + * + * @param message 消息信息 + * @param userIds 用户ID列表 + * @return 结果 + */ + int sendAutoMessage(SysMessageBo message, List userIds); + + /** + * 标记消息为已读 + * + * @param messageId 消息ID + * @param userId 用户ID + * @return 结果 + */ + int markAsRead(Long messageId, Long userId); + + /** + * 查询用户未读消息分页列表 + * + * @param userId 用户ID + * @param pageQuery 分页参数 + * @return 消息分页列表 + */ + TableDataInfo selectPageUnreadMessages(Long userId, PageQuery pageQuery); + + /** + * 查询用户已读消息分页列表 + * + * @param userId 用户ID + * @param pageQuery 分页参数 + * @return 消息分页列表 + */ + TableDataInfo selectPageReadMessages(Long userId, PageQuery pageQuery); + + /** + * 查询消息列表 + * + * @param message 消息信息 + * @return 消息列表 + */ + List selectMessageList(SysMessageBo message); + + /** + * 查询消息详细信息 + * + * @param id 消息ID + * @return 消息信息 + */ + SysMessageVo selectMessageById(Long id); + + /** + * 新增消息 + * + * @param message 消息信息 + * @return 结果 + */ + int insertMessage(SysMessageBo message); + + /** + * 修改消息 + * + * @param message 消息信息 + * @return 结果 + */ + int updateMessage(SysMessageBo message); + + /** + * 批量删除消息 + * + * @param ids 需要删除的消息ID数组 + * @return 结果 + */ + int deleteMessageByIds(Long[] ids); + + /** + * 删除消息信息 + * + * @param id 消息ID + * @return 结果 + */ + int deleteMessageById(Long id); + + /** + * 将BO对象转换为查询条件 + * + * @param bo 业务对象 + * @return 查询条件 + */ + LambdaQueryWrapper toWrapper(SysMessageBo bo); + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageTemplateService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageTemplateService.java new file mode 100644 index 000000000..c53b0ec44 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageTemplateService.java @@ -0,0 +1,75 @@ +package org.dromara.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.SysMessageTemplate; +import org.dromara.system.domain.bo.SysMessageTemplateBo; +import org.dromara.system.domain.vo.SysMessageTemplateVo; + +import java.util.List; + +/** + * 消息模板Service接口 + * + * @author ruoyi + */ +public interface ISysMessageTemplateService extends IService { + + /** + * 查询消息模板列表 + * + * @param template 消息模板信息 + * @return 消息模板列表 + */ + List selectTemplateList(SysMessageTemplateBo template); + + /** + * 查询消息模板分页列表 + * + * @param template 消息模板信息 + * @param pageQuery 分页参数 + * @return 消息模板分页列表 + */ + TableDataInfo selectTemplatePage(SysMessageTemplateBo template, PageQuery pageQuery); + + /** + * 查询消息模板详细信息 + * + * @param id 消息模板ID + * @return 消息模板信息 + */ + SysMessageTemplateVo selectTemplateById(Long id); + + /** + * 新增消息模板 + * + * @param template 消息模板信息 + * @return 结果 + */ + int insertTemplate(SysMessageTemplateBo template); + + /** + * 修改消息模板 + * + * @param template 消息模板信息 + * @return 结果 + */ + int updateTemplate(SysMessageTemplateBo template); + + /** + * 批量删除消息模板 + * + * @param ids 需要删除的消息模板ID数组 + * @return 结果 + */ + int deleteTemplateByIds(Long[] ids); + + /** + * 删除消息模板信息 + * + * @param id 消息模板ID + * @return 结果 + */ + int deleteTemplateById(Long id); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/MessageWebSocketServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/MessageWebSocketServiceImpl.java new file mode 100644 index 000000000..9af04a16c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/MessageWebSocketServiceImpl.java @@ -0,0 +1,31 @@ +package org.dromara.system.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.system.domain.vo.SysMessageVo; +import org.dromara.system.service.IMessageWebSocketService; +import org.dromara.system.websocket.MessageWebSocketServer; +import org.springframework.stereotype.Service; + +/** + * WebSocket消息服务实现类 + * + * @author ruoyi + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MessageWebSocketServiceImpl implements IMessageWebSocketService { + + private final MessageWebSocketServer messageWebSocketServer; + + @Override + public void sendMessage(Long userId, SysMessageVo message) { + messageWebSocketServer.sendMessage(userId, String.valueOf(message)); + } + + @Override + public void broadcastMessage(SysMessageVo message) { + messageWebSocketServer.broadcastMessage(String.valueOf(message)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java new file mode 100644 index 000000000..1578590cb --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java @@ -0,0 +1,208 @@ +package org.dromara.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.SysMessageUser; +import org.dromara.system.domain.bo.SysMessageBo; +import org.dromara.system.domain.event.MessageEvent; +import org.dromara.system.domain.vo.SysMessageVo; +import org.dromara.system.mapper.SysMessageMapper; +import org.dromara.system.mapper.SysMessageUserMapper; +import org.dromara.system.service.ISysMessageService; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 消息Service业务层处理 + * + * @author ruoyi + */ +@Service +@RequiredArgsConstructor +public class SysMessageServiceImpl extends ServiceImpl implements ISysMessageService { + + private final SysMessageMapper messageMapper; + private final SysMessageUserMapper messageUserMapper; + private final ApplicationEventPublisher eventPublisher; + + @Override + public TableDataInfo selectPageMessageList(SysMessageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.like(StringUtils.isNotBlank(bo.getTitle()), SysMessage::getTitle, bo.getTitle()) + .eq(StringUtils.isNotBlank(bo.getMsgType()), SysMessage::getMsgType, bo.getMsgType()) +// .eq(StringUtils.isNotBlank(bo.getStatus()), SysMessage::getStatus, bo.getStatus()) + .orderByDesc(SysMessage::getCreateTime); + Page page = messageMapper.selectPage(pageQuery.build(), lqw); + List records = MapstructUtils.convert(page.getRecords(), SysMessageVo.class); + Page voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); + voPage.setRecords(records); + return TableDataInfo.build(voPage); + } + + @Override + public Page selectUnreadMessagesPage(Long userId, Page page) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.inSql(SysMessage::getId, + "SELECT message_id FROM sys_message_user WHERE user_id = " + userId + " AND is_read = 0") + .orderByDesc(SysMessage::getCreateTime); + + Page messagePage = messageMapper.selectPage(page, lqw); + Page voPage = new Page<>(messagePage.getCurrent(), messagePage.getSize(), messagePage.getTotal()); + voPage.setRecords(MapstructUtils.convert(messagePage.getRecords(), SysMessageVo.class)); + return voPage; + } + + @Override + public Page selectReadMessagesPage(Long userId, Page page) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.inSql(SysMessage::getId, + "SELECT message_id FROM sys_message_user WHERE user_id = " + userId + " AND is_read = 1") + .orderByDesc(SysMessage::getCreateTime); + + Page messagePage = messageMapper.selectPage(page, lqw); + Page voPage = new Page<>(messagePage.getCurrent(), messagePage.getSize(), messagePage.getTotal()); + voPage.setRecords(MapstructUtils.convert(messagePage.getRecords(), SysMessageVo.class)); + return voPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int sendMessageToUser(SysMessageBo message, Long userId) { + // 保存消息 + SysMessage entity = message.toEntity(); + messageMapper.insert(entity); + // 创建消息用户关联 + SysMessageUser messageUser = new SysMessageUser(); + messageUser.setMessageId(entity.getId()); + messageUser.setUserId(userId); + messageUser.setIsRead(false); + int rows = messageUserMapper.insert(messageUser); + // 发送WebSocket消息 + if (rows > 0) { + SysMessageVo messageVo = MapstructUtils.convert(entity, SysMessageVo.class); + eventPublisher.publishEvent(new MessageEvent(this, messageVo, userId)); + } + return rows; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int sendMessageToUsers(SysMessageBo message, List userIds) { + // 保存消息 + SysMessage entity = message.toEntity(); + messageMapper.insert(entity); + // 批量创建消息用户关联 + int count = 0; + for (Long userId : userIds) { + SysMessageUser messageUser = new SysMessageUser(); + messageUser.setMessageId(entity.getId()); + messageUser.setUserId(userId); + messageUser.setIsRead(false); + int rows = messageUserMapper.insert(messageUser); + if (rows > 0) { + count++; + // 发送WebSocket消息 + SysMessageVo messageVo = MapstructUtils.convert(entity, SysMessageVo.class); + eventPublisher.publishEvent(new MessageEvent(this, messageVo, userId)); + } + } + return count; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int sendAutoMessage(SysMessageBo message, List userIds) { + message.setMsgType("AUTO"); + return sendMessageToUsers(message, userIds); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int markAsRead(Long messageId, Long userId) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(SysMessageUser::getMessageId, messageId) + .eq(SysMessageUser::getUserId, userId); + SysMessageUser messageUser = new SysMessageUser(); + messageUser.setIsRead(true); + return messageUserMapper.update(messageUser, lqw); + } + + @Override + public TableDataInfo selectPageUnreadMessages(Long userId, PageQuery pageQuery) { + Page page = messageMapper.selectPageUnreadMessages(pageQuery.build(), userId); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo selectPageReadMessages(Long userId, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); +// lqw.eq(SysMessage::getStatus, "0") +// .inSql(SysMessage::getId, +// "SELECT message_id FROM sys_message_user WHERE user_id = " + userId + " AND is_read = 1") +// .orderByDesc(SysMessage::getCreateTime); +// + Page page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize()); + Page resultPage = messageMapper.selectPage(page, lqw); + List records = MapstructUtils.convert(resultPage.getRecords(), SysMessageVo.class); + Page voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal()); + voPage.setRecords(records); + return TableDataInfo.build(voPage); + } + + @Override + public List selectMessageList(SysMessageBo message) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(StringUtils.isNotBlank(message.getMsgType()), SysMessage::getMsgType, message.getMsgType()) + .eq(StringUtils.isNotBlank(message.getSubType()), SysMessage::getSubType, message.getSubType()) + .eq(message.getSenderId() != null, SysMessage::getSenderId, message.getSenderId()) +// .eq(StringUtils.isNotBlank(message.getStatus()), SysMessage::getStatus, message.getStatus()) + .orderByDesc(SysMessage::getCreateTime); + return MapstructUtils.convert(messageMapper.selectList(lqw), SysMessageVo.class); + } + + @Override + public SysMessageVo selectMessageById(Long id) { + return MapstructUtils.convert(messageMapper.selectById(id), SysMessageVo.class); + } + + @Override + public int insertMessage(SysMessageBo message) { + return messageMapper.insert(message.toEntity()); + } + + @Override + public int updateMessage(SysMessageBo message) { + return messageMapper.updateById(message.toEntity()); + } + + @Override + public int deleteMessageByIds(Long[] ids) { + return messageMapper.deleteBatchIds(List.of(ids)); + } + + @Override + public int deleteMessageById(Long id) { + return messageMapper.deleteById(id); + } + + @Override + public LambdaQueryWrapper toWrapper(SysMessageBo bo) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.like(StringUtils.isNotBlank(bo.getTitle()), SysMessage::getTitle, bo.getTitle()) + .eq(StringUtils.isNotBlank(bo.getMsgType()), SysMessage::getMsgType, bo.getMsgType()) + .orderByDesc(SysMessage::getCreateTime); + return lqw; + } + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageTemplateServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageTemplateServiceImpl.java new file mode 100644 index 000000000..589841bb6 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageTemplateServiceImpl.java @@ -0,0 +1,83 @@ +package org.dromara.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.SysMessageTemplate; +import org.dromara.system.domain.bo.SysMessageTemplateBo; +import org.dromara.system.domain.vo.SysMessageTemplateVo; +import org.dromara.system.mapper.SysMessageTemplateMapper; +import org.dromara.system.service.ISysMessageTemplateService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 消息模板Service业务层处理 + * + * @author ruoyi + */ +@RequiredArgsConstructor +@Service +public class SysMessageTemplateServiceImpl extends ServiceImpl implements ISysMessageTemplateService { + + private final SysMessageTemplateMapper templateMapper; + + @Override + public TableDataInfo selectTemplatePage(SysMessageTemplateBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(StringUtils.isNotBlank(bo.getTemplateType()), SysMessageTemplate::getTemplateType, bo.getTemplateType()) +// .eq(StringUtils.isNotBlank(bo.getStatus()), SysMessageTemplate::getStatus, bo.getStatus()) + .like(StringUtils.isNotBlank(bo.getTemplateName()), SysMessageTemplate::getTemplateName, bo.getTemplateName()) + .like(StringUtils.isNotBlank(bo.getTemplateCode()), SysMessageTemplate::getTemplateCode, bo.getTemplateCode()) + .orderByDesc(SysMessageTemplate::getCreateTime); + + Page page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize()); + Page resultPage = templateMapper.selectPage(page, lqw); + List records = MapstructUtils.convert(resultPage.getRecords(), SysMessageTemplateVo.class); + Page voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal()); + voPage.setRecords(records); + return TableDataInfo.build(voPage); + } + + @Override + public List selectTemplateList(SysMessageTemplateBo bo) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(StringUtils.isNotBlank(bo.getTemplateType()), SysMessageTemplate::getTemplateType, bo.getTemplateType()) +// .eq(StringUtils.isNotBlank(bo.getStatus()), SysMessageTemplate::getStatus, bo.getStatus()) + .like(StringUtils.isNotBlank(bo.getTemplateName()), SysMessageTemplate::getTemplateName, bo.getTemplateName()) + .like(StringUtils.isNotBlank(bo.getTemplateCode()), SysMessageTemplate::getTemplateCode, bo.getTemplateCode()) + .orderByDesc(SysMessageTemplate::getCreateTime); + return MapstructUtils.convert(templateMapper.selectList(lqw), SysMessageTemplateVo.class); + } + + @Override + public SysMessageTemplateVo selectTemplateById(Long id) { + return MapstructUtils.convert(templateMapper.selectById(id), SysMessageTemplateVo.class); + } + + @Override + public int insertTemplate(SysMessageTemplateBo bo) { + return templateMapper.insert(bo.toEntity()); + } + + @Override + public int updateTemplate(SysMessageTemplateBo bo) { + return templateMapper.updateById(bo.toEntity()); + } + + @Override + public int deleteTemplateByIds(Long[] ids) { + return templateMapper.deleteBatchIds(List.of(ids)); + } + + @Override + public int deleteTemplateById(Long id) { + return templateMapper.deleteById(id); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java index a73415c64..ac115f736 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java @@ -179,7 +179,7 @@ public class SysTenantServiceImpl implements ISysTenantService { for (SysDictType dictType : dictTypeList) { dictType.setDictId(null); dictType.setTenantId(tenantId); - dictType.setCreateDept(null); +// dictType.setCreateDept(null); dictType.setCreateBy(null); dictType.setCreateTime(null); dictType.setUpdateBy(null); @@ -188,7 +188,7 @@ public class SysTenantServiceImpl implements ISysTenantService { for (SysDictData dictData : dictDataList) { dictData.setDictCode(null); dictData.setTenantId(tenantId); - dictData.setCreateDept(null); +// dictData.setCreateDept(null); dictData.setCreateBy(null); dictData.setCreateTime(null); dictData.setUpdateBy(null); @@ -202,7 +202,7 @@ public class SysTenantServiceImpl implements ISysTenantService { for (SysConfig config : sysConfigList) { config.setConfigId(null); config.setTenantId(tenantId); - config.setCreateDept(null); +// config.setCreateDept(null); config.setCreateBy(null); config.setCreateTime(null); config.setUpdateBy(null); @@ -448,7 +448,7 @@ public class SysTenantServiceImpl implements ISysTenantService { data.setTenantId(tenantId); data.setCreateTime(null); data.setUpdateTime(null); - data.setCreateDept(null); +// data.setCreateDept(null); data.setCreateBy(null); data.setUpdateBy(null); set.add(tenantId); @@ -472,7 +472,7 @@ public class SysTenantServiceImpl implements ISysTenantService { data.setTenantId(tenantId); data.setCreateTime(null); data.setUpdateTime(null); - data.setCreateDept(null); +// data.setCreateDept(null); data.setCreateBy(null); data.setUpdateBy(null); set.add(tenantId); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/task/MessageScheduledTask.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/task/MessageScheduledTask.java new file mode 100644 index 000000000..9c4778efe --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/task/MessageScheduledTask.java @@ -0,0 +1,78 @@ +package org.dromara.system.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.system.domain.SysMessage; +//import org.dromara.system.domain.SysMessageUser; +import org.dromara.system.domain.SysMessageUser; +import org.dromara.system.domain.vo.SysMessageVo; +import org.dromara.system.mapper.SysMessageMapper; +//import org.dromara.system.mapper.SysMessageUserMapper +import org.dromara.system.mapper.SysMessageUserMapper; +import org.dromara.system.websocket.MessageWebSocketServer; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 消息定时任务 + * + * @author ruoyi + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class MessageScheduledTask { + + private final SysMessageMapper messageMapper; + private final SysMessageUserMapper messageUserMapper; + private final MessageWebSocketServer messageWebSocketServer; + + /** + * 每分钟执行一次,处理待发送的消息 + */ + @Scheduled(cron = "0 */1 * * * ?") + @Transactional(rollbackFor = Exception.class) + public void processScheduledMessages() { + log.info("开始处理定时消息..."); + try { + // 查询待发送的消息 + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); +// lqw.eq(SysMessage::getStatus, "0") +// .le(SysMessage::getScheduledTime, LocalDateTime.now()) +// .isNotNull(SysMessage::getScheduledTime); + + List messages = messageMapper.selectList(lqw); + + for (SysMessage message : messages) { + // 查询消息接收者 + LambdaQueryWrapper userLqw = new LambdaQueryWrapper<>(); + userLqw.eq(SysMessageUser::getMessageId, message.getId()); + List messageUsers = messageUserMapper.selectList(userLqw); + + // 发送消息 + SysMessageVo messageVo = MapstructUtils.convert(message, SysMessageVo.class); + for (SysMessageUser messageUser : messageUsers) { + try { + messageWebSocketServer.sendMessage(messageUser.getUserId(), String.valueOf(messageVo)); + } catch (Exception e) { + log.error("发送消息失败,消息ID:{},用户ID:{}", message.getId(), messageUser.getUserId(), e); + } + } + + // 更新消息状态为已发送 +// message.setStatus("1"); + messageMapper.updateById(message); + } + + log.info("定时消息处理完成,共处理{}条消息", messages.size()); + } catch (Exception e) { + log.error("处理定时消息时发生错误", e); + } + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/websocket/MessageWebSocketServer.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/websocket/MessageWebSocketServer.java new file mode 100644 index 000000000..03ce98449 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/websocket/MessageWebSocketServer.java @@ -0,0 +1,116 @@ +package org.dromara.system.websocket; + +import jakarta.websocket.*; +import jakarta.websocket.server.ServerEndpoint; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.websocket.holder.WebSocketSessionHolder; +import org.dromara.common.websocket.utils.WebSocketUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 站内信 WebSocket 服务 + */ +@Slf4j +@Component +@ServerEndpoint("/websocket/message") +public class MessageWebSocketServer { + + /** + * 用户ID和会话的映射关系 + */ + private static final Map SESSION_MAP = new ConcurrentHashMap<>(); + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session) { + log.info("[WebSocket] 新的连接建立,sessionId: {}", session.getId()); + try { + // 获取当前登录用户 + LoginUser loginUser = (LoginUser) session.getUserProperties().get("loginUser"); + if (loginUser != null) { + SESSION_MAP.put(loginUser.getUserId(), session); + log.info("[WebSocket] 用户 {} 连接成功", loginUser.getUserId()); + } else { + log.warn("[WebSocket] 未获取到用户信息,关闭连接"); + session.close(); + } + } catch (Exception e) { + log.error("[WebSocket] 连接建立失败", e); + } + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose(Session session) { + log.info("[WebSocket] 连接关闭,sessionId: {}", session.getId()); + // 从映射中移除会话 + SESSION_MAP.values().removeIf(s -> s.getId().equals(session.getId())); + } + + /** + * 收到客户端消息后调用的方法 + */ + @OnMessage + public void onMessage(String message, Session session) { + log.info("[WebSocket] 收到消息: {}", message); + // 这里可以处理客户端发送的消息 + } + + /** + * 发生错误时调用 + */ + @OnError + public void onError(Session session, Throwable error) { + log.error("[WebSocket] 发生错误", error); + // 从映射中移除会话 + SESSION_MAP.values().removeIf(s -> s.getId().equals(session.getId())); + } + + /** + * 发送消息给指定用户 + * + * @param userId 用户ID + * @param message 消息内容 + */ + public void sendMessage(Long userId, String message) { + Session session = SESSION_MAP.get(userId); + if (session != null && session.isOpen()) { + try { + session.getBasicRemote().sendText(message); + log.info("[WebSocket] 消息发送成功,接收者: {}", userId); + } catch (IOException e) { + log.error("[WebSocket] 消息发送失败", e); + } + } else { + log.warn("[WebSocket] 用户 {} 不在线或会话已关闭", userId); + } + } + + /** + * 广播消息给所有在线用户 + * + * @param message 消息内容 + */ + public void broadcastMessage(String message) { + SESSION_MAP.forEach((userId, session) -> { + if (session.isOpen()) { + try { + session.getBasicRemote().sendText(message); + log.info("[WebSocket] 广播消息发送成功,接收者: {}", userId); + } catch (IOException e) { + log.error("[WebSocket] 广播消息发送失败,接收者: {}", userId, e); + } + } + }); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md deleted file mode 100644 index c938b1e50..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md +++ /dev/null @@ -1,3 +0,0 @@ -java包使用 `.` 分割 resource 目录使用 `/` 分割 -
-此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageMapper.xml new file mode 100644 index 000000000..bc5135bf9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageUserMapper.xml new file mode 100644 index 000000000..c89c93e52 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageUserMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + select id, message_id, user_id, is_read, read_time, create_time, update_time + from sys_message_user + + + + update sys_message_user + set is_read = #{isRead}, + read_time = sysdate() + where message_id = #{messageId} + and user_id = #{userId} + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index ded6fa858..fdd5e84a4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -69,5 +69,19 @@ select count(*) from sys_user where del_flag = '0' and user_id = #{userId} + + +