Commit e9223bf3406ddb651590ad044ea0c57849bb14d2
1 parent
dc302772
主体代码编写完成
Showing
21 changed files
with
552 additions
and
82 deletions
fw-hestia-common/src/main/java/cn/fw/hestia/common/constant/MessageStr.java
@@ -9,4 +9,6 @@ package cn.fw.hestia.common.constant; | @@ -9,4 +9,6 @@ package cn.fw.hestia.common.constant; | ||
9 | public interface MessageStr { | 9 | public interface MessageStr { |
10 | String QUERY_FAILURE = "查询失败"; | 10 | String QUERY_FAILURE = "查询失败"; |
11 | String SAVE_FAILURE = "操作失败"; | 11 | String SAVE_FAILURE = "操作失败"; |
12 | + | ||
13 | + String SUCCEED_STR = "succeed"; | ||
12 | } | 14 | } |
fw-hestia-domain/src/main/java/cn/fw/hestia/domain/db/MessageHistory.java
1 | package cn.fw.hestia.domain.db; | 1 | package cn.fw.hestia.domain.db; |
2 | 2 | ||
3 | -import cn.fw.hestia.domain.enums.SendStateEnum; | 3 | +import cn.fw.hestia.domain.enums.MessageStateEnum; |
4 | import com.baomidou.mybatisplus.annotation.*; | 4 | import com.baomidou.mybatisplus.annotation.*; |
5 | import com.baomidou.mybatisplus.extension.activerecord.Model; | 5 | import com.baomidou.mybatisplus.extension.activerecord.Model; |
6 | import lombok.Data; | 6 | import lombok.Data; |
@@ -65,7 +65,11 @@ public class MessageHistory extends Model<MessageHistory> { | @@ -65,7 +65,11 @@ public class MessageHistory extends Model<MessageHistory> { | ||
65 | /** | 65 | /** |
66 | * 发送状态 | 66 | * 发送状态 |
67 | */ | 67 | */ |
68 | - private SendStateEnum sendState; | 68 | + private MessageStateEnum state; |
69 | + /** | ||
70 | + * 是否有效 | ||
71 | + */ | ||
72 | + private Boolean yn; | ||
69 | /** | 73 | /** |
70 | * 创建时间 | 74 | * 创建时间 |
71 | */ | 75 | */ |
fw-hestia-domain/src/main/java/cn/fw/hestia/domain/enums/SendStateEnum.java renamed to fw-hestia-domain/src/main/java/cn/fw/hestia/domain/enums/MessageStateEnum.java
@@ -11,15 +11,15 @@ import lombok.Getter; | @@ -11,15 +11,15 @@ import lombok.Getter; | ||
11 | * @description : 发送状态 | 11 | * @description : 发送状态 |
12 | * @date: 2020-08-11 17:37 | 12 | * @date: 2020-08-11 17:37 |
13 | */ | 13 | */ |
14 | -public enum SendStateEnum implements IEnum<Integer> { | 14 | +public enum MessageStateEnum implements IEnum<Integer> { |
15 | /** | 15 | /** |
16 | - * 等待回调 | 16 | + * 未处理 |
17 | */ | 17 | */ |
18 | - OK(1, "发送成功"), | 18 | + MADA(1, "未处理"), |
19 | /** | 19 | /** |
20 | - * 完成回调 | 20 | + * 已处理 |
21 | */ | 21 | */ |
22 | - FAIL(2, "发送失败"), | 22 | + SUMI(2, "已处理"), |
23 | ; | 23 | ; |
24 | 24 | ||
25 | /** | 25 | /** |
@@ -32,7 +32,7 @@ public enum SendStateEnum implements IEnum<Integer> { | @@ -32,7 +32,7 @@ public enum SendStateEnum implements IEnum<Integer> { | ||
32 | @Getter | 32 | @Getter |
33 | private final String name; | 33 | private final String name; |
34 | 34 | ||
35 | - SendStateEnum(final Integer value, final String name) { | 35 | + MessageStateEnum(final Integer value, final String name) { |
36 | this.value = value; | 36 | this.value = value; |
37 | this.name = name; | 37 | this.name = name; |
38 | } | 38 | } |
@@ -41,8 +41,8 @@ public enum SendStateEnum implements IEnum<Integer> { | @@ -41,8 +41,8 @@ public enum SendStateEnum implements IEnum<Integer> { | ||
41 | * 根据枚举值获取枚举对象 | 41 | * 根据枚举值获取枚举对象 |
42 | */ | 42 | */ |
43 | @JsonCreator | 43 | @JsonCreator |
44 | - public static SendStateEnum ofValue(final Integer value) { | ||
45 | - for (final SendStateEnum stateEnum : SendStateEnum.values()) { | 44 | + public static MessageStateEnum ofValue(final Integer value) { |
45 | + for (final MessageStateEnum stateEnum : MessageStateEnum.values()) { | ||
46 | if (stateEnum.value.equals(value)) { | 46 | if (stateEnum.value.equals(value)) { |
47 | return stateEnum; | 47 | return stateEnum; |
48 | } | 48 | } |
@@ -68,7 +68,7 @@ public enum SendStateEnum implements IEnum<Integer> { | @@ -68,7 +68,7 @@ public enum SendStateEnum implements IEnum<Integer> { | ||
68 | */ | 68 | */ |
69 | @JsonCreator | 69 | @JsonCreator |
70 | public static String getNameByVale(final Integer value) { | 70 | public static String getNameByVale(final Integer value) { |
71 | - for (final SendStateEnum stateEnum : SendStateEnum.values()) { | 71 | + for (final MessageStateEnum stateEnum : MessageStateEnum.values()) { |
72 | if (stateEnum.value.equals(value)) { | 72 | if (stateEnum.value.equals(value)) { |
73 | return stateEnum.getName(); | 73 | return stateEnum.getName(); |
74 | } | 74 | } |
fw-hestia-domain/src/main/java/cn/fw/hestia/domain/vo/Demo.java deleted
fw-hestia-domain/src/main/java/cn/fw/hestia/domain/vo/HistoryQuery.java
0 → 100644
1 | +package cn.fw.hestia.domain.vo; | ||
2 | + | ||
3 | +import cn.fw.common.page.BasePageQuery; | ||
4 | +import cn.fw.common.web.annotation.LoginContextField; | ||
5 | +import lombok.Data; | ||
6 | +import lombok.EqualsAndHashCode; | ||
7 | +import lombok.ToString; | ||
8 | + | ||
9 | +/** | ||
10 | + * @author : kurisu | ||
11 | + * @className : HistoryQuery | ||
12 | + * @description : 查询条件 | ||
13 | + * @date: 2021-09-25 10:45 | ||
14 | + */ | ||
15 | +@Data | ||
16 | +@ToString(callSuper = true) | ||
17 | +@EqualsAndHashCode(callSuper = true) | ||
18 | +public class HistoryQuery extends BasePageQuery { | ||
19 | + | ||
20 | + @LoginContextField(LoginContextField.Name.USER_ID) | ||
21 | + private Long memberId; | ||
22 | +} |
fw-hestia-domain/src/main/java/cn/fw/hestia/domain/vo/MessageHistoryVO.java
0 → 100644
1 | +package cn.fw.hestia.domain.vo; | ||
2 | + | ||
3 | +import cn.fw.hestia.domain.db.MessageHistory; | ||
4 | +import lombok.Data; | ||
5 | +import lombok.ToString; | ||
6 | + | ||
7 | +import java.util.Date; | ||
8 | +import java.util.Objects; | ||
9 | + | ||
10 | +/** | ||
11 | + * @author : kurisu | ||
12 | + * @className : Demo | ||
13 | + * @description : | ||
14 | + * @date: 2021-09-23 15:26 | ||
15 | + */ | ||
16 | +@Data | ||
17 | +@ToString | ||
18 | +public class MessageHistoryVO { | ||
19 | + private Long messageId; | ||
20 | + /** | ||
21 | + * 会员id | ||
22 | + */ | ||
23 | + private Long memberId; | ||
24 | + /** | ||
25 | + * 消息内容 | ||
26 | + */ | ||
27 | + private String title; | ||
28 | + /** | ||
29 | + * 备注 | ||
30 | + */ | ||
31 | + private String remark; | ||
32 | + /** | ||
33 | + * 是否已读 | ||
34 | + */ | ||
35 | + private Boolean readz; | ||
36 | + /** | ||
37 | + * 发送时间 | ||
38 | + */ | ||
39 | + private Date messageTime; | ||
40 | + | ||
41 | + public static MessageHistoryVO with(MessageHistory history) { | ||
42 | + if (Objects.isNull(history)) { | ||
43 | + return null; | ||
44 | + } | ||
45 | + MessageHistoryVO vo = new MessageHistoryVO(); | ||
46 | + vo.setMessageId(history.getId()); | ||
47 | + vo.setMemberId(history.getMemberId()); | ||
48 | + vo.setTitle(history.getTitle()); | ||
49 | + vo.setReadz(history.getReadz()); | ||
50 | + vo.setRemark(history.getRemark()); | ||
51 | + vo.setMessageTime(history.getCreateTime()); | ||
52 | + return vo; | ||
53 | + } | ||
54 | +} |
fw-hestia-rpc/src/main/java/cn/fw/hestia/rpc/passport/TemplateMessageService.java
1 | package cn.fw.hestia.rpc.passport; | 1 | package cn.fw.hestia.rpc.passport; |
2 | 2 | ||
3 | import cn.fw.data.base.domain.common.Message; | 3 | import cn.fw.data.base.domain.common.Message; |
4 | +import cn.fw.hestia.common.constant.MessageStr; | ||
4 | import cn.fw.hestia.common.utils.StringUtils; | 5 | import cn.fw.hestia.common.utils.StringUtils; |
5 | import cn.fw.hestia.rpc.passport.dto.TMParam; | 6 | import cn.fw.hestia.rpc.passport.dto.TMParam; |
6 | import cn.fw.passport.sdk.api.WxMpTemplateMessageApi; | 7 | import cn.fw.passport.sdk.api.WxMpTemplateMessageApi; |
7 | import cn.fw.passport.sdk.api.param.WxMpTempMessageData; | 8 | import cn.fw.passport.sdk.api.param.WxMpTempMessageData; |
8 | import cn.fw.passport.sdk.api.param.WxMpTempMessageParam; | 9 | import cn.fw.passport.sdk.api.param.WxMpTempMessageParam; |
9 | -import lombok.Getter; | 10 | +import com.alibaba.fastjson.JSONArray; |
10 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
11 | import org.springframework.stereotype.Service; | 12 | import org.springframework.stereotype.Service; |
12 | 13 | ||
13 | -import java.util.Arrays; | ||
14 | import java.util.List; | 14 | import java.util.List; |
15 | 15 | ||
16 | /** | 16 | /** |
@@ -31,12 +31,6 @@ public class TemplateMessageService { | @@ -31,12 +31,6 @@ public class TemplateMessageService { | ||
31 | } | 31 | } |
32 | 32 | ||
33 | /** | 33 | /** |
34 | - * 消息模板Code | ||
35 | - */ | ||
36 | - @Getter | ||
37 | - private final String templateCode = "OPENTM412432053"; | ||
38 | - | ||
39 | - /** | ||
40 | * 发送消息通知 | 34 | * 发送消息通知 |
41 | * | 35 | * |
42 | * @param messageParam | 36 | * @param messageParam |
@@ -46,34 +40,33 @@ public class TemplateMessageService { | @@ -46,34 +40,33 @@ public class TemplateMessageService { | ||
46 | try { | 40 | try { |
47 | WxMpTempMessageParam param = new WxMpTempMessageParam(); | 41 | WxMpTempMessageParam param = new WxMpTempMessageParam(); |
48 | param.setCusId(messageParam.getMemberId()); | 42 | param.setCusId(messageParam.getMemberId()); |
49 | - param.setTempCode(getTemplateCode()); | 43 | + param.setTempCode(messageParam.getTemplateCode()); |
50 | WxMpTempMessageData remark = new WxMpTempMessageData(); | 44 | WxMpTempMessageData remark = new WxMpTempMessageData(); |
51 | remark.setValue(messageParam.getRemark()); | 45 | remark.setValue(messageParam.getRemark()); |
52 | - remark.setColor("#a61b29"); | ||
53 | param.setRemark(remark); | 46 | param.setRemark(remark); |
54 | param.setTitle(new WxMpTempMessageData(messageParam.getTitle())); | 47 | param.setTitle(new WxMpTempMessageData(messageParam.getTitle())); |
55 | - List<WxMpTempMessageData> keywords = Arrays.asList( | ||
56 | - new WxMpTempMessageData(messageParam.getChangeType()), | ||
57 | - //门店 | ||
58 | - new WxMpTempMessageData(messageParam.getChangeResult()) | ||
59 | - //到期时间 | ||
60 | - ); | ||
61 | - param.setKeyWordList(keywords); | ||
62 | - param.setPagePath(getPagePath(messageParam.getPath(), messageParam.getSceneToken())); | 48 | + if (StringUtils.isValid(messageParam.getKeywords())) { |
49 | + List<WxMpTempMessageData> keywords = JSONArray.parseArray(messageParam.getKeywords(), WxMpTempMessageData.class); | ||
50 | + param.setKeyWordList(keywords); | ||
51 | + } | ||
52 | + String pagePath = getPagePath(messageParam.getPath(), messageParam.getSceneToken()); | ||
53 | + if (StringUtils.isValid(pagePath)) { | ||
54 | + param.setPagePath(pagePath); | ||
55 | + } | ||
63 | Message<?> msg = wxMpTemplateMessageApi.send(param); | 56 | Message<?> msg = wxMpTemplateMessageApi.send(param); |
64 | if (!msg.isSuccess()) { | 57 | if (!msg.isSuccess()) { |
65 | log.error("【passport系统】发送模板消息失败:{}", msg.getResult()); | 58 | log.error("【passport系统】发送模板消息失败:{}", msg.getResult()); |
66 | return msg.getResult(); | 59 | return msg.getResult(); |
67 | } | 60 | } |
68 | - return ""; | 61 | + return MessageStr.SUCCEED_STR; |
69 | } catch (Exception e) { | 62 | } catch (Exception e) { |
70 | log.error("发送模板消息失败", e); | 63 | log.error("发送模板消息失败", e); |
71 | - return "系统异常"; | 64 | + return "系统调用异常"; |
72 | } | 65 | } |
73 | } | 66 | } |
74 | 67 | ||
75 | 68 | ||
76 | - private String getPagePath(String path, String sceneToken) { | 69 | + private String getPagePath(String path, Long sceneToken) { |
77 | if (StringUtils.isEmpty(path)) { | 70 | if (StringUtils.isEmpty(path)) { |
78 | return null; | 71 | return null; |
79 | } | 72 | } |
fw-hestia-rpc/src/main/java/cn/fw/hestia/rpc/passport/dto/TMParam.java
@@ -28,26 +28,20 @@ public class TMParam { | @@ -28,26 +28,20 @@ public class TMParam { | ||
28 | * 标题 | 28 | * 标题 |
29 | */ | 29 | */ |
30 | private String title; | 30 | private String title; |
31 | + | ||
32 | + private String keywords; | ||
31 | /** | 33 | /** |
32 | * 备注 | 34 | * 备注 |
33 | */ | 35 | */ |
34 | private String remark; | 36 | private String remark; |
35 | - /** | ||
36 | - * 变更类型 | ||
37 | - */ | ||
38 | - private String changeType; | ||
39 | - /** | ||
40 | - * 变更结果 | ||
41 | - */ | ||
42 | - private String changeResult; | 37 | + |
43 | /** | 38 | /** |
44 | * 如需跳转小程序,则是小程序页面路径 | 39 | * 如需跳转小程序,则是小程序页面路径 |
45 | */ | 40 | */ |
46 | private String path; | 41 | private String path; |
47 | - /** | ||
48 | - * 跳转小程序所携带的参数 | ||
49 | - */ | ||
50 | - private Map<String, String> paramMap; | ||
51 | 42 | ||
52 | - private String sceneToken; | 43 | + |
44 | + private Long sceneToken; | ||
45 | + | ||
46 | + private String templateCode; | ||
53 | } | 47 | } |
fw-hestia-sdk/src/main/java/cn/fw/hestia/sdk/api/IMessageCenterService.java
@@ -3,10 +3,12 @@ package cn.fw.hestia.sdk.api; | @@ -3,10 +3,12 @@ package cn.fw.hestia.sdk.api; | ||
3 | import cn.fw.data.base.domain.common.Message; | 3 | import cn.fw.data.base.domain.common.Message; |
4 | import cn.fw.hestia.sdk.params.TemplateMessageParam; | 4 | import cn.fw.hestia.sdk.params.TemplateMessageParam; |
5 | import org.springframework.cloud.openfeign.FeignClient; | 5 | import org.springframework.cloud.openfeign.FeignClient; |
6 | +import org.springframework.web.bind.annotation.DeleteMapping; | ||
6 | import org.springframework.web.bind.annotation.PostMapping; | 7 | import org.springframework.web.bind.annotation.PostMapping; |
7 | import org.springframework.web.bind.annotation.RequestBody; | 8 | import org.springframework.web.bind.annotation.RequestBody; |
8 | 9 | ||
9 | import javax.validation.Valid; | 10 | import javax.validation.Valid; |
11 | +import javax.validation.constraints.NotNull; | ||
10 | 12 | ||
11 | /** | 13 | /** |
12 | * @author : kurisu | 14 | * @author : kurisu |
@@ -16,19 +18,22 @@ import javax.validation.Valid; | @@ -16,19 +18,22 @@ import javax.validation.Valid; | ||
16 | */ | 18 | */ |
17 | @FeignClient(value = "fw-hestia", path = "/api/hestia/mc") | 19 | @FeignClient(value = "fw-hestia", path = "/api/hestia/mc") |
18 | public interface IMessageCenterService { | 20 | public interface IMessageCenterService { |
21 | + | ||
19 | /** | 22 | /** |
20 | - * 异步发送模板消息 | 23 | + * 发送模板消息 |
24 | + * | ||
21 | * @param templateMessageParam | 25 | * @param templateMessageParam |
22 | * @return | 26 | * @return |
23 | */ | 27 | */ |
24 | - @PostMapping("/asynSend") | ||
25 | - Message<Long> asynSend(@Valid @RequestBody TemplateMessageParam templateMessageParam); | 28 | + @PostMapping("/send") |
29 | + Message<Long> send(@Valid @RequestBody TemplateMessageParam templateMessageParam); | ||
26 | 30 | ||
27 | /** | 31 | /** |
28 | - * 同步 | ||
29 | - * @param templateMessageParam | 32 | + * 撤回消息 |
33 | + * | ||
34 | + * @param sceneToken | ||
30 | * @return | 35 | * @return |
31 | */ | 36 | */ |
32 | - @PostMapping("/send") | ||
33 | - Message<Long> send(@Valid @RequestBody TemplateMessageParam templateMessageParam); | 37 | + @DeleteMapping("/send") |
38 | + Message<Boolean> revokeMessage(@NotNull(message = "Token不能为空") Long sceneToken); | ||
34 | } | 39 | } |
fw-hestia-sdk/src/main/java/cn/fw/hestia/sdk/params/TemplateMessageParam.java
@@ -3,6 +3,8 @@ package cn.fw.hestia.sdk.params; | @@ -3,6 +3,8 @@ package cn.fw.hestia.sdk.params; | ||
3 | import lombok.Data; | 3 | import lombok.Data; |
4 | import lombok.ToString; | 4 | import lombok.ToString; |
5 | 5 | ||
6 | +import javax.validation.constraints.NotBlank; | ||
7 | +import javax.validation.constraints.NotNull; | ||
6 | import java.util.Map; | 8 | import java.util.Map; |
7 | 9 | ||
8 | /** | 10 | /** |
@@ -15,6 +17,12 @@ import java.util.Map; | @@ -15,6 +17,12 @@ import java.util.Map; | ||
15 | * 变更结果:{{keyword2.DATA}} | 17 | * 变更结果:{{keyword2.DATA}} |
16 | * {{remark.DATA}} | 18 | * {{remark.DATA}} |
17 | * | 19 | * |
20 | + * 示例: | ||
21 | + * 尊敬的客户,您的业务状态已经发生变更 变更详情如下: | ||
22 | + * 变更类型:组局地点 | ||
23 | + * 变更结果:由清水河改为滨江 | ||
24 | + * 如有疑问,请拨打客服电话 | ||
25 | + * | ||
18 | * @author kurisu | 26 | * @author kurisu |
19 | */ | 27 | */ |
20 | @Data | 28 | @Data |
@@ -23,10 +31,12 @@ public class TemplateMessageParam { | @@ -23,10 +31,12 @@ public class TemplateMessageParam { | ||
23 | /** | 31 | /** |
24 | * 会员id | 32 | * 会员id |
25 | */ | 33 | */ |
34 | + @NotNull(message = "会员id不能为空") | ||
26 | private Long memberId; | 35 | private Long memberId; |
27 | /** | 36 | /** |
28 | - * 标题 对应「first」字段 | 37 | + * 标题内容 对应「first」字段 |
29 | */ | 38 | */ |
39 | + @NotBlank(message = "标题内容不能为空") | ||
30 | private String title; | 40 | private String title; |
31 | /** | 41 | /** |
32 | * 备注 | 42 | * 备注 |
@@ -35,10 +45,12 @@ public class TemplateMessageParam { | @@ -35,10 +45,12 @@ public class TemplateMessageParam { | ||
35 | /** | 45 | /** |
36 | * 变更类型 | 46 | * 变更类型 |
37 | */ | 47 | */ |
48 | + @NotBlank(message = "变更类型不能为空") | ||
38 | private String changeType; | 49 | private String changeType; |
39 | /** | 50 | /** |
40 | * 变更结果 | 51 | * 变更结果 |
41 | */ | 52 | */ |
53 | + @NotBlank(message = "变更结果不能为空") | ||
42 | private String changeResult; | 54 | private String changeResult; |
43 | /** | 55 | /** |
44 | * 如需跳转小程序,则是小程序页面路径 | 56 | * 如需跳转小程序,则是小程序页面路径 |
fw-hestia-sdk/src/main/java/cn/fw/hestia/sdk/result/MessageSendMq.java
0 → 100644
1 | +package cn.fw.hestia.sdk.result; | ||
2 | + | ||
3 | +import lombok.AllArgsConstructor; | ||
4 | +import lombok.Data; | ||
5 | +import lombok.NoArgsConstructor; | ||
6 | + | ||
7 | +import java.util.Date; | ||
8 | + | ||
9 | +/** | ||
10 | + * @author : kurisu | ||
11 | + * @className : MessageSendMQ | ||
12 | + * @description : 消息发送成功的通知 | ||
13 | + * @date: 2021-09-25 09:27 | ||
14 | + */ | ||
15 | +@Data | ||
16 | +@AllArgsConstructor | ||
17 | +@NoArgsConstructor | ||
18 | +public class MessageSendMq { | ||
19 | + public final static String TOPIC = "message_send"; | ||
20 | + /** | ||
21 | + * 唯一标识 | ||
22 | + */ | ||
23 | + private Long sceneToken; | ||
24 | + /** | ||
25 | + * 发送时间 | ||
26 | + */ | ||
27 | + private Date sendTime; | ||
28 | +} |
fw-hestia-server/src/main/java/cn/fw/hestia/server/controller/api/MessageCenterServiceImpl.java
@@ -4,13 +4,15 @@ import cn.fw.common.web.annotation.ControllerMethod; | @@ -4,13 +4,15 @@ import cn.fw.common.web.annotation.ControllerMethod; | ||
4 | import cn.fw.data.base.domain.common.Message; | 4 | import cn.fw.data.base.domain.common.Message; |
5 | import cn.fw.hestia.sdk.api.IMessageCenterService; | 5 | import cn.fw.hestia.sdk.api.IMessageCenterService; |
6 | import cn.fw.hestia.sdk.params.TemplateMessageParam; | 6 | import cn.fw.hestia.sdk.params.TemplateMessageParam; |
7 | +import cn.fw.hestia.service.buz.MessageCenterBizService; | ||
7 | import lombok.extern.slf4j.Slf4j; | 8 | import lombok.extern.slf4j.Slf4j; |
8 | -import org.springframework.web.bind.annotation.PostMapping; | ||
9 | -import org.springframework.web.bind.annotation.RequestBody; | ||
10 | -import org.springframework.web.bind.annotation.RequestMapping; | ||
11 | -import org.springframework.web.bind.annotation.RestController; | 9 | +import org.springframework.beans.factory.annotation.Autowired; |
10 | +import org.springframework.web.bind.annotation.*; | ||
12 | 11 | ||
13 | import javax.validation.Valid; | 12 | import javax.validation.Valid; |
13 | +import javax.validation.constraints.NotNull; | ||
14 | + | ||
15 | +import static cn.fw.common.web.util.ResultBuilder.success; | ||
14 | 16 | ||
15 | /** | 17 | /** |
16 | * @author : kurisu | 18 | * @author : kurisu |
@@ -22,18 +24,24 @@ import javax.validation.Valid; | @@ -22,18 +24,24 @@ import javax.validation.Valid; | ||
22 | @RestController | 24 | @RestController |
23 | @RequestMapping("/api/hestia/mc") | 25 | @RequestMapping("/api/hestia/mc") |
24 | public class MessageCenterServiceImpl implements IMessageCenterService { | 26 | public class MessageCenterServiceImpl implements IMessageCenterService { |
27 | + private final MessageCenterBizService messageCenterBizService; | ||
25 | 28 | ||
26 | - @PostMapping("/asynSend") | ||
27 | - @Override | ||
28 | - @ControllerMethod("保存进站记录") | ||
29 | - public Message<Long> asynSend(@Valid @RequestBody TemplateMessageParam templateMessageParam) { | ||
30 | - return null; | 29 | + @Autowired |
30 | + public MessageCenterServiceImpl(final MessageCenterBizService messageCenterBizService) { | ||
31 | + this.messageCenterBizService = messageCenterBizService; | ||
31 | } | 32 | } |
32 | 33 | ||
33 | @PostMapping("/send") | 34 | @PostMapping("/send") |
34 | @Override | 35 | @Override |
35 | - @ControllerMethod("保存进站记录") | 36 | + @ControllerMethod("发送模板消息") |
36 | public Message<Long> send(@Valid @RequestBody TemplateMessageParam templateMessageParam) { | 37 | public Message<Long> send(@Valid @RequestBody TemplateMessageParam templateMessageParam) { |
37 | - return null; | 38 | + return success(messageCenterBizService.saveMessage(templateMessageParam)); |
39 | + } | ||
40 | + | ||
41 | + @Override | ||
42 | + @DeleteMapping("/revoke") | ||
43 | + @ControllerMethod("撤回消息") | ||
44 | + public Message<Boolean> revokeMessage(@NotNull(message = "Token不能为空") Long sceneToken) { | ||
45 | + return success(messageCenterBizService.revokeMessage(sceneToken)); | ||
38 | } | 46 | } |
39 | } | 47 | } |
fw-hestia-server/src/main/java/cn/fw/hestia/server/controller/wx/MessageCenterController.java
0 → 100644
1 | +package cn.fw.hestia.server.controller.wx; | ||
2 | + | ||
3 | +import cn.fw.common.page.AppPage; | ||
4 | +import cn.fw.common.web.annotation.ControllerMethod; | ||
5 | +import cn.fw.common.web.auth.PassportAuthBean; | ||
6 | +import cn.fw.common.web.auth.annotation.CurrentUser; | ||
7 | +import cn.fw.data.base.domain.common.Message; | ||
8 | +import cn.fw.hestia.domain.vo.HistoryQuery; | ||
9 | +import cn.fw.hestia.domain.vo.MessageHistoryVO; | ||
10 | +import cn.fw.hestia.service.buz.MessageCenterBizService; | ||
11 | +import cn.fw.security.auth.client.annotation.Authorization; | ||
12 | +import cn.fw.security.auth.client.enums.AuthType; | ||
13 | +import lombok.extern.slf4j.Slf4j; | ||
14 | +import org.springframework.beans.factory.annotation.Autowired; | ||
15 | +import org.springframework.validation.annotation.Validated; | ||
16 | +import org.springframework.web.bind.annotation.GetMapping; | ||
17 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
18 | +import org.springframework.web.bind.annotation.RestController; | ||
19 | + | ||
20 | +import javax.validation.constraints.NotNull; | ||
21 | +import java.util.Map; | ||
22 | + | ||
23 | +import static cn.fw.common.web.util.ResultBuilder.success; | ||
24 | + | ||
25 | +/** | ||
26 | + * @author : kurisu | ||
27 | + * @className : MessageCenterController | ||
28 | + * @description : | ||
29 | + * @date: 2021-09-23 15:39 | ||
30 | + */ | ||
31 | +@Slf4j | ||
32 | +@Validated | ||
33 | +@RestController | ||
34 | +@Authorization(AuthType.WECHAT) | ||
35 | +@RequestMapping("/wx/message") | ||
36 | +public class MessageCenterController { | ||
37 | + private final MessageCenterBizService messageCenterBizService; | ||
38 | + | ||
39 | + @Autowired | ||
40 | + public MessageCenterController(final MessageCenterBizService messageCenterBizService) { | ||
41 | + this.messageCenterBizService = messageCenterBizService; | ||
42 | + } | ||
43 | + | ||
44 | + @GetMapping("/unread") | ||
45 | + @ControllerMethod("查询消息未读数") | ||
46 | + public Message<Integer> send(@CurrentUser PassportAuthBean user) { | ||
47 | + return success(messageCenterBizService.unreadCount(user.getUserId())); | ||
48 | + } | ||
49 | + | ||
50 | + @GetMapping("/query/pageParams") | ||
51 | + @ControllerMethod("查询小程序页面的参数") | ||
52 | + public Message<Map<String, String>> queryPageParams(@NotNull(message = "sceneToken不能为空") final Long sceneToken) { | ||
53 | + return success(messageCenterBizService.queryPageParams(sceneToken)); | ||
54 | + } | ||
55 | + | ||
56 | + @GetMapping("/query/history") | ||
57 | + @ControllerMethod("查询消息历史记录") | ||
58 | + public Message<AppPage<MessageHistoryVO>> queryHistory(HistoryQuery query) { | ||
59 | + return success(messageCenterBizService.queryHistory(query)); | ||
60 | + } | ||
61 | +} |
fw-hestia-server/src/main/java/cn/fw/hestia/server/task/SendMessageTask.java
0 → 100644
1 | +package cn.fw.hestia.server.task; | ||
2 | + | ||
3 | +import cn.fw.hestia.domain.db.MessageHistory; | ||
4 | +import cn.fw.hestia.domain.enums.MessageStateEnum; | ||
5 | +import cn.fw.hestia.service.buz.MessageCenterBizService; | ||
6 | +import cn.fw.hestia.service.data.MessageHistoryService; | ||
7 | +import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||
8 | +import org.springframework.beans.factory.annotation.Autowired; | ||
9 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
10 | +import org.springframework.scheduling.annotation.Scheduled; | ||
11 | +import org.springframework.stereotype.Component; | ||
12 | +import org.springframework.transaction.annotation.Transactional; | ||
13 | +import org.springframework.util.CollectionUtils; | ||
14 | + | ||
15 | +import java.util.Date; | ||
16 | +import java.util.List; | ||
17 | + | ||
18 | +/** | ||
19 | + * @author : kurisu | ||
20 | + * @className : FollowNoticeTask | ||
21 | + * @description : 服务号消息定时任务 | ||
22 | + * @date: 2020-08-25 16:57 | ||
23 | + */ | ||
24 | +@Component | ||
25 | +@ConditionalOnProperty(prefix = "task", name = "switch", havingValue = "on") | ||
26 | +public class SendMessageTask { | ||
27 | + private final MessageCenterBizService messageCenterBizService; | ||
28 | + private final MessageHistoryService messageHistoryService; | ||
29 | + | ||
30 | + private final static int MAX_FREQUENCY = 5; | ||
31 | + | ||
32 | + @Autowired | ||
33 | + public SendMessageTask(final MessageCenterBizService messageCenterBizService, | ||
34 | + final MessageHistoryService messageHistoryService) { | ||
35 | + this.messageCenterBizService = messageCenterBizService; | ||
36 | + this.messageHistoryService = messageHistoryService; | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * 发送模板消息 | ||
41 | + */ | ||
42 | + @Scheduled(initialDelay = 1000 * 10, fixedRate = 1000 * 10) | ||
43 | + @Transactional(rollbackFor = Exception.class) | ||
44 | + public void sendNotice() { | ||
45 | + List<MessageHistory> list = messageHistoryService.list(Wrappers.<MessageHistory>lambdaQuery() | ||
46 | + .eq(MessageHistory::getState, MessageStateEnum.MADA) | ||
47 | + .eq(MessageHistory::getYn, Boolean.TRUE) | ||
48 | + .lt(MessageHistory::getFrequency, MAX_FREQUENCY) | ||
49 | + .lt(MessageHistory::getSendTime, new Date()) | ||
50 | + ); | ||
51 | + if (CollectionUtils.isEmpty(list)) { | ||
52 | + return; | ||
53 | + } | ||
54 | + for (MessageHistory history : list) { | ||
55 | + messageCenterBizService.sendMessage(history); | ||
56 | + } | ||
57 | + } | ||
58 | +} |
fw-hestia-server/src/main/resources/application.yml
@@ -37,7 +37,7 @@ spring: | @@ -37,7 +37,7 @@ spring: | ||
37 | max-wait: 1S | 37 | max-wait: 1S |
38 | min-idle: 3 | 38 | min-idle: 3 |
39 | port: 6379 | 39 | port: 6379 |
40 | - database: 3 | 40 | + database: 4 |
41 | rest-result-processor: | 41 | rest-result-processor: |
42 | enabled: false | 42 | enabled: false |
43 | servlet: | 43 | servlet: |
@@ -91,7 +91,7 @@ jedis: | @@ -91,7 +91,7 @@ jedis: | ||
91 | test-while-idle: true | 91 | test-while-idle: true |
92 | host: 192.168.0.8 | 92 | host: 192.168.0.8 |
93 | port: 6379 | 93 | port: 6379 |
94 | - db: 3 | 94 | + db: 4 |
95 | mybatis-plus: | 95 | mybatis-plus: |
96 | configuration: | 96 | configuration: |
97 | cache-enabled: false | 97 | cache-enabled: false |
fw-hestia-service/src/main/java/cn/fw/hestia/component/SendMsgProducer.java
0 → 100644
1 | +package cn.fw.hestia.component; | ||
2 | + | ||
3 | +import cn.fw.hestia.sdk.result.MessageSendMq; | ||
4 | +import lombok.extern.slf4j.Slf4j; | ||
5 | +import org.apache.rocketmq.spring.core.RocketMQTemplate; | ||
6 | +import org.springframework.beans.factory.annotation.Autowired; | ||
7 | +import org.springframework.stereotype.Component; | ||
8 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
9 | + | ||
10 | +/** | ||
11 | + * @author kurisu | ||
12 | + */ | ||
13 | +@Slf4j | ||
14 | +@Component | ||
15 | +public class SendMsgProducer { | ||
16 | + @Autowired | ||
17 | + private RocketMQTemplate rocketMQTemplate; | ||
18 | + | ||
19 | + @RequestMapping(value = "send") | ||
20 | + public void send(MessageSendMq messageSendMq) { | ||
21 | + try { | ||
22 | + log.info("模板消息发送成功mq: body:[{}]", messageSendMq); | ||
23 | + rocketMQTemplate.syncSend(MessageSendMq.TOPIC + ":*", messageSendMq); | ||
24 | + } catch (Exception e) { | ||
25 | + e.printStackTrace(); | ||
26 | + } | ||
27 | + } | ||
28 | +} | ||
0 | \ No newline at end of file | 29 | \ No newline at end of file |
fw-hestia-service/src/main/java/cn/fw/hestia/service/buz/MessageCenterBizService.java
0 → 100644
1 | +package cn.fw.hestia.service.buz; | ||
2 | + | ||
3 | +import cn.fw.common.data.mybatis.pagination.PageData; | ||
4 | +import cn.fw.common.page.AppPage; | ||
5 | +import cn.fw.hestia.common.constant.MessageStr; | ||
6 | +import cn.fw.hestia.common.utils.DateUtil; | ||
7 | +import cn.fw.hestia.common.utils.StringUtils; | ||
8 | +import cn.fw.hestia.component.SendMsgProducer; | ||
9 | +import cn.fw.hestia.domain.db.MessageHistory; | ||
10 | +import cn.fw.hestia.domain.db.SendLog; | ||
11 | +import cn.fw.hestia.domain.enums.MessageStateEnum; | ||
12 | +import cn.fw.hestia.domain.vo.HistoryQuery; | ||
13 | +import cn.fw.hestia.domain.vo.MessageHistoryVO; | ||
14 | +import cn.fw.hestia.rpc.passport.TemplateMessageService; | ||
15 | +import cn.fw.hestia.rpc.passport.dto.TMParam; | ||
16 | +import cn.fw.hestia.sdk.params.TemplateMessageParam; | ||
17 | +import cn.fw.hestia.sdk.result.MessageSendMq; | ||
18 | +import cn.fw.hestia.service.data.MessageHistoryService; | ||
19 | +import cn.fw.hestia.service.data.SendLogService; | ||
20 | +import cn.fw.passport.sdk.api.param.WxMpTempMessageData; | ||
21 | +import com.alibaba.fastjson.JSON; | ||
22 | +import com.alibaba.fastjson.JSONArray; | ||
23 | +import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||
24 | +import lombok.Getter; | ||
25 | +import lombok.RequiredArgsConstructor; | ||
26 | +import lombok.extern.slf4j.Slf4j; | ||
27 | +import org.springframework.stereotype.Service; | ||
28 | +import org.springframework.transaction.annotation.Transactional; | ||
29 | +import org.springframework.util.CollectionUtils; | ||
30 | + | ||
31 | +import java.util.*; | ||
32 | +import java.util.stream.Collectors; | ||
33 | + | ||
34 | +import static cn.fw.common.businessvalidator.Validator.BV; | ||
35 | + | ||
36 | +/** | ||
37 | + * @author : kurisu | ||
38 | + * @className : MessageCenterBizService | ||
39 | + * @description : 消息中心处理类 | ||
40 | + * @date: 2021-09-24 17:10 | ||
41 | + */ | ||
42 | +@Service | ||
43 | +@RequiredArgsConstructor | ||
44 | +@Slf4j | ||
45 | +public class MessageCenterBizService { | ||
46 | + private final MessageHistoryService messageHistoryService; | ||
47 | + private final SendLogService sendLogService; | ||
48 | + private final TemplateMessageService templateMessageService; | ||
49 | + private final SendMsgProducer sendMsgProducer; | ||
50 | + | ||
51 | + /** | ||
52 | + * 消息模板Code | ||
53 | + */ | ||
54 | + @Getter | ||
55 | + private final String templateCode = "OPENTM412432053"; | ||
56 | + | ||
57 | + /** | ||
58 | + * 发送 | ||
59 | + * | ||
60 | + * @param templateMessageParam | ||
61 | + * @return token | ||
62 | + */ | ||
63 | + @Transactional(rollbackFor = Exception.class) | ||
64 | + public Long saveMessage(TemplateMessageParam templateMessageParam) { | ||
65 | + MessageHistory messageHistory = createBean(templateMessageParam); | ||
66 | + messageHistoryService.save(messageHistory); | ||
67 | + return messageHistory.getId(); | ||
68 | + } | ||
69 | + | ||
70 | + | ||
71 | + /** | ||
72 | + * 发送模板消息 | ||
73 | + * | ||
74 | + * @param history | ||
75 | + */ | ||
76 | + @Transactional(rollbackFor = Exception.class) | ||
77 | + public void sendMessage(MessageHistory history) { | ||
78 | + TMParam tmParam = createTmParam(history); | ||
79 | + String result = templateMessageService.sendTemplateMessage(tmParam); | ||
80 | + boolean succeed = MessageStr.SUCCEED_STR.equals(result); | ||
81 | + final int frequency = history.getFrequency() + 1; | ||
82 | + history.setFrequency(frequency); | ||
83 | + if (succeed) { | ||
84 | + history.setState(MessageStateEnum.SUMI); | ||
85 | + //发送成功发送mq | ||
86 | + sendMsgProducer.send(new MessageSendMq(history.getId(), new Date())); | ||
87 | + } else { | ||
88 | + history.setSendTime(DateUtil.getExpired(history.getSendTime(), 1 << frequency, Calendar.MINUTE)); | ||
89 | + } | ||
90 | + messageHistoryService.updateById(history); | ||
91 | + SendLog sendLog = new SendLog(); | ||
92 | + sendLog.setMessageId(history.getId()); | ||
93 | + sendLog.setSendTime(new Date()); | ||
94 | + sendLog.setSucceed(succeed); | ||
95 | + sendLog.setResult(result); | ||
96 | + sendLogService.save(sendLog); | ||
97 | + } | ||
98 | + | ||
99 | + /** | ||
100 | + * 撤回消息 | ||
101 | + * | ||
102 | + * @param sceneToken | ||
103 | + * @return | ||
104 | + */ | ||
105 | + @Transactional(rollbackFor = Exception.class) | ||
106 | + public Boolean revokeMessage(Long sceneToken) { | ||
107 | + MessageHistory history = messageHistoryService.getById(sceneToken); | ||
108 | + if (Objects.isNull(history)) { | ||
109 | + return Boolean.TRUE; | ||
110 | + } | ||
111 | + history.setYn(Boolean.FALSE); | ||
112 | + return messageHistoryService.updateById(history); | ||
113 | + } | ||
114 | + | ||
115 | + /** | ||
116 | + * 查询未读数 | ||
117 | + * | ||
118 | + * @param memberId | ||
119 | + * @return | ||
120 | + */ | ||
121 | + public int unreadCount(Long memberId) { | ||
122 | + return messageHistoryService.count(Wrappers.<MessageHistory>lambdaQuery() | ||
123 | + .eq(MessageHistory::getMemberId, memberId) | ||
124 | + .eq(MessageHistory::getReadz, Boolean.FALSE) | ||
125 | + .eq(MessageHistory::getYn, Boolean.TRUE) | ||
126 | + ); | ||
127 | + } | ||
128 | + | ||
129 | + /** | ||
130 | + * 查询页面所需参数 | ||
131 | + * | ||
132 | + * @param sceneToken | ||
133 | + * @return | ||
134 | + */ | ||
135 | + public Map<String, String> queryPageParams(Long sceneToken) { | ||
136 | + MessageHistory history = messageHistoryService.getById(sceneToken); | ||
137 | + BV.notNull(history, () -> "消息不存在或者已经被撤销"); | ||
138 | + readMessage(history); | ||
139 | + String pageParams = history.getPageParams(); | ||
140 | + if (StringUtils.isEmpty(pageParams)) { | ||
141 | + return new HashMap<>(0); | ||
142 | + } | ||
143 | + return JSON.<HashMap<String, String>>parseObject(pageParams, HashMap.class); | ||
144 | + } | ||
145 | + | ||
146 | + /** | ||
147 | + * 查询历史消息记录 | ||
148 | + * @param query | ||
149 | + * @return | ||
150 | + */ | ||
151 | + public AppPage<MessageHistoryVO> queryHistory(HistoryQuery query) { | ||
152 | + PageData<MessageHistory> pageData = messageHistoryService.page(new PageData<>(query), Wrappers.<MessageHistory>lambdaQuery() | ||
153 | + .eq(MessageHistory::getMemberId, query.getMemberId()) | ||
154 | + .eq(MessageHistory::getYn, Boolean.TRUE) | ||
155 | + .orderByDesc(MessageHistory::getCreateTime) | ||
156 | + ); | ||
157 | + AppPage<MessageHistoryVO> page = AppPage.empty(query); | ||
158 | + page.setCurrent((int) pageData.getCurrent()); | ||
159 | + page.setPageSize((int) pageData.getSize()); | ||
160 | + page.setTotal(pageData.getTotal()); | ||
161 | + page.setData(new ArrayList<>()); | ||
162 | + if (!CollectionUtils.isEmpty(pageData.getRecords())) { | ||
163 | + List<MessageHistoryVO> voList = pageData.getRecords().stream().map(MessageHistoryVO::with).collect(Collectors.toList()); | ||
164 | + page.setData(voList); | ||
165 | + } | ||
166 | + return page; | ||
167 | + } | ||
168 | + | ||
169 | + @Transactional(rollbackFor = Exception.class) | ||
170 | + protected void readMessage(MessageHistory history) { | ||
171 | + if (Boolean.FALSE.equals(history.getReadz())) { | ||
172 | + history.setReadz(Boolean.TRUE); | ||
173 | + messageHistoryService.updateById(history); | ||
174 | + } | ||
175 | + } | ||
176 | + | ||
177 | + private MessageHistory createBean(TemplateMessageParam param) { | ||
178 | + MessageHistory messageHistory = new MessageHistory(); | ||
179 | + messageHistory.setMemberId(param.getMemberId()); | ||
180 | + messageHistory.setTemplateCode(getTemplateCode()); | ||
181 | + messageHistory.setTitle(param.getTitle()); | ||
182 | + messageHistory.setRemark(param.getRemark()); | ||
183 | + messageHistory.setPagePath(param.getPath()); | ||
184 | + messageHistory.setReadz(Boolean.FALSE); | ||
185 | + messageHistory.setFrequency(0); | ||
186 | + messageHistory.setYn(Boolean.TRUE); | ||
187 | + messageHistory.setSendTime(new Date()); | ||
188 | + messageHistory.setState(MessageStateEnum.MADA); | ||
189 | + List<WxMpTempMessageData> keywords = Arrays.asList( | ||
190 | + new WxMpTempMessageData(param.getChangeType()), | ||
191 | + new WxMpTempMessageData(param.getChangeResult()) | ||
192 | + ); | ||
193 | + messageHistory.setKeywords(JSONArray.toJSONString(keywords)); | ||
194 | + if (!CollectionUtils.isEmpty(param.getParamMap())) { | ||
195 | + messageHistory.setPageParams(JSON.toJSONString(param.getParamMap())); | ||
196 | + } | ||
197 | + return messageHistory; | ||
198 | + } | ||
199 | + | ||
200 | + private TMParam createTmParam(MessageHistory history) { | ||
201 | + TMParam tmParam = new TMParam(); | ||
202 | + tmParam.setSceneToken(history.getId()); | ||
203 | + tmParam.setTitle(history.getTitle()); | ||
204 | + tmParam.setRemark(history.getRemark()); | ||
205 | + tmParam.setPath(history.getPagePath()); | ||
206 | + tmParam.setTemplateCode(history.getTemplateCode()); | ||
207 | + tmParam.setMemberId(history.getMemberId()); | ||
208 | + tmParam.setKeywords(history.getKeywords()); | ||
209 | + return tmParam; | ||
210 | + } | ||
211 | +} |
fw-hestia-service/src/main/java/cn/fw/hestia/service/MessageHistoryService.java renamed to fw-hestia-service/src/main/java/cn/fw/hestia/service/data/MessageHistoryService.java
fw-hestia-service/src/main/java/cn/fw/hestia/service/SendLogService.java renamed to fw-hestia-service/src/main/java/cn/fw/hestia/service/data/SendLogService.java
fw-hestia-service/src/main/java/cn/fw/hestia/service/impl/MessageHistoryServiceImpl.java renamed to fw-hestia-service/src/main/java/cn/fw/hestia/service/data/impl/MessageHistoryServiceImpl.java
1 | -package cn.fw.hestia.service.impl; | 1 | +package cn.fw.hestia.service.data.impl; |
2 | 2 | ||
3 | import cn.fw.hestia.dao.MessageHistoryDao; | 3 | import cn.fw.hestia.dao.MessageHistoryDao; |
4 | import cn.fw.hestia.domain.db.MessageHistory; | 4 | import cn.fw.hestia.domain.db.MessageHistory; |
5 | -import cn.fw.hestia.service.MessageHistoryService; | 5 | +import cn.fw.hestia.service.data.MessageHistoryService; |
6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
7 | import org.springframework.stereotype.Service; | 7 | import org.springframework.stereotype.Service; |
8 | 8 |
fw-hestia-service/src/main/java/cn/fw/hestia/service/impl/SendLogServiceImpl.java renamed to fw-hestia-service/src/main/java/cn/fw/hestia/service/data/impl/SendLogServiceImpl.java
1 | -package cn.fw.hestia.service.impl; | 1 | +package cn.fw.hestia.service.data.impl; |
2 | 2 | ||
3 | import cn.fw.hestia.dao.SendLogDao; | 3 | import cn.fw.hestia.dao.SendLogDao; |
4 | import cn.fw.hestia.domain.db.SendLog; | 4 | import cn.fw.hestia.domain.db.SendLog; |
5 | -import cn.fw.hestia.service.SendLogService; | 5 | +import cn.fw.hestia.service.data.SendLogService; |
6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
7 | import org.springframework.stereotype.Service; | 7 | import org.springframework.stereotype.Service; |
8 | 8 |