From 97b3bb7d641253d53b912730746f8a8d6de48d9b Mon Sep 17 00:00:00 2001 From: xinlaa <1570728529@qq.com> Date: Thu, 7 Jan 2021 15:23:06 +0800 Subject: [PATCH 001/177] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=BE=A4=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=87=8D=E7=BD=AE=E9=97=AE=E9=A2=98=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linkwe-ui/src/views/customerManage/group.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/linkwe-ui/src/views/customerManage/group.vue b/linkwe-ui/src/views/customerManage/group.vue index 71c88642d..37bc12a7c 100644 --- a/linkwe-ui/src/views/customerManage/group.vue +++ b/linkwe-ui/src/views/customerManage/group.vue @@ -37,6 +37,9 @@ export default { if (this.dateRange[0]) { this.query.beginTime = this.dateRange[0] this.query.endTime = this.dateRange[1] + } else { + this.query.beginTime = '' + this.query.endTime = '' } page && (this.query.pageNum = page) this.loading = true @@ -102,14 +105,14 @@ export default { class="top-search" label-width="80px" > - + - + Date: Fri, 8 Jan 2021 01:12:16 +0800 Subject: [PATCH 002/177] =?UTF-8?q?=E8=81=8A=E5=A4=A9=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=A0=8F=E6=8E=A5=E5=8F=A3=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linkwe-ui/src/api/appTool/chatBar.js | 52 +++++++++ .../src/views/appTool/chatToolbar/list.vue | 106 +++++++++++++----- linkwe-ui/src/views/customerManage/group.vue | 1 - 3 files changed, 129 insertions(+), 30 deletions(-) create mode 100644 linkwe-ui/src/api/appTool/chatBar.js diff --git a/linkwe-ui/src/api/appTool/chatBar.js b/linkwe-ui/src/api/appTool/chatBar.js new file mode 100644 index 000000000..04331c342 --- /dev/null +++ b/linkwe-ui/src/api/appTool/chatBar.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' +const service = window.CONFIG.services.wecom + '/chat' + +/** + * + * @param {*} params + */ +export function getList(params) { + return request({ + url: service + '/side/list', + params: { + h5: 0, + }, + }) +} + +/** + * 更新侧边栏信息 + * @param {*} data + * { + "sideId": "", + "mediaType": "0 图片(image)、1 语音(voice)、2 视频(video),3 普通文件(file) 4 文本 5 海报", + "sideName": "聊天工具栏名称", + "total": "已抓取素材数量", + "using": "是否启用 0 启用 1 未启用" +} + */ +export function update(data) { + return request({ + url: service + '/side', + method: 'put', + data, + }) +} + +/** + * 侧边栏抓取素材 + * @param {*} data + * { + "sideId": "", + "materialIds": "素材id列表", + "mediaType": "素材类型 0 图片(image)、1 语音(voice)、2 视频(video),3 普通文件(file) 4 文本 5 海报", + "checkAll": "是否全选 0 全选 1 非全选" +} + */ +export function getMaterial(data) { + return request({ + url: service + '/item', + method: 'POST', + data, + }) +} diff --git a/linkwe-ui/src/views/appTool/chatToolbar/list.vue b/linkwe-ui/src/views/appTool/chatToolbar/list.vue index 0d1a60967..2827d6b97 100644 --- a/linkwe-ui/src/views/appTool/chatToolbar/list.vue +++ b/linkwe-ui/src/views/appTool/chatToolbar/list.vue @@ -1,25 +1,72 @@ - + + + -- Gitee From 4f7ce4539d588c917ef9a732160c9e00b7b10635 Mon Sep 17 00:00:00 2001 From: xinlaa <1570728529@qq.com> Date: Sat, 16 Jan 2021 23:45:42 +0800 Subject: [PATCH 029/177] --- linkwe-ui/src/views/appTool/chatToolbar/list.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/linkwe-ui/src/views/appTool/chatToolbar/list.vue b/linkwe-ui/src/views/appTool/chatToolbar/list.vue index 8a1327926..8bee3f04c 100644 --- a/linkwe-ui/src/views/appTool/chatToolbar/list.vue +++ b/linkwe-ui/src/views/appTool/chatToolbar/list.vue @@ -120,9 +120,9 @@ export default { v-model="row.sideName" placeholder="请输入" > -
+ {{ row.sideName }} -
+ -- Gitee From 5700a5ff609499649fdfe651b3f02f0604190b5d Mon Sep 17 00:00:00 2001 From: 18356073052 <274131322@qq.com> Date: Sun, 17 Jan 2021 22:53:53 +0800 Subject: [PATCH 030/177] =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E6=A0=8F=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=85=B3=E9=94=AE=E8=AF=8D=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/wecom/WeChatItemController.java | 6 ++++-- .../linkwechat/wecom/mapper/WeChatItemMapper.java | 8 ++++++-- .../wecom/service/IWeChatItemService.java | 2 +- .../wecom/service/impl/WeChatItemServiceImpl.java | 4 ++-- .../resources/mapper/wecom/WeChatItemMapper.xml | 15 +++++++++------ 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeChatItemController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeChatItemController.java index 0f0c00483..8be7250cc 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeChatItemController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeChatItemController.java @@ -16,6 +16,7 @@ import java.util.List; /** * 聊天工具侧边栏 + * * @author kewen */ @RequestMapping(value = "/wecom/chat/item") @@ -41,9 +42,10 @@ public class WeChatItemController extends BaseController { */ //@PreAuthorize("@ss.hasPermi('chat:item:list')") @GetMapping("/list") - public TableDataInfo list(@RequestParam(value = "sideId") Long sideId) { + public TableDataInfo list(@RequestParam(value = "sideId") Long sideId + , @RequestParam(value = "keyword", required = false) String keyword) { startPage(); - List weChatSideVos = weChatItemService.chatItems(sideId); + List weChatSideVos = weChatItemService.chatItems(sideId,keyword); return getDataTable(weChatSideVos); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeChatItemMapper.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeChatItemMapper.java index 992b9a472..791e8bcc8 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeChatItemMapper.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeChatItemMapper.java @@ -10,12 +10,14 @@ import java.util.List; /** * 聊天工具侧边栏 + * * @author kewen */ public interface WeChatItemMapper extends BaseMapper { /** * 批量添加侧边栏素材 + * * @param items 侧边栏素材列表 * @return 结果 */ @@ -23,6 +25,7 @@ public interface WeChatItemMapper extends BaseMapper { /** * 删除侧边栏素材 + * * @param sideId 侧边栏id * @return 结果 */ @@ -32,8 +35,9 @@ public interface WeChatItemMapper extends BaseMapper { /** * h5素材列表 * - * @param sideId 侧边栏id + * @param sideId 侧边栏id + * @param keyword 关键词 * @return */ - public List findChatItems(@Param("sideId") Long sideId); + public List findChatItems(@Param("sideId") Long sideId, @Param("keyword") String keyword); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeChatItemService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeChatItemService.java index 38f50de5d..23a5473ea 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeChatItemService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeChatItemService.java @@ -27,6 +27,6 @@ public interface IWeChatItemService extends IService { * @param sideId 侧边栏id * @return */ - public List chatItems(Long sideId); + public List chatItems(Long sideId,String keyword); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeChatItemServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeChatItemServiceImpl.java index 5fa495a84..237f2dcd4 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeChatItemServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeChatItemServiceImpl.java @@ -96,8 +96,8 @@ public class WeChatItemServiceImpl extends ServiceImpl chatItems(Long sideId) { - return weChatItemMapper.findChatItems(sideId); + public List chatItems(Long sideId,String keyword) { + return weChatItemMapper.findChatItems(sideId,keyword); } /** diff --git a/linkwe-wecom/src/main/resources/mapper/wecom/WeChatItemMapper.xml b/linkwe-wecom/src/main/resources/mapper/wecom/WeChatItemMapper.xml index d7376236a..6a410f6b3 100644 --- a/linkwe-wecom/src/main/resources/mapper/wecom/WeChatItemMapper.xml +++ b/linkwe-wecom/src/main/resources/mapper/wecom/WeChatItemMapper.xml @@ -5,7 +5,7 @@ INSERT INTO - we_chat_item(item_id,side_id,material_id,create_by,create_time,update_by,update_time) + we_chat_item(item_id,side_id,material_id,create_by,create_time,update_by,update_time) VALUES @@ -14,11 +14,11 @@ - DELETE FROM - we_chat_item - - side_id=#{sideId} - + DELETE FROM + we_chat_item + + side_id=#{sideId} + \ No newline at end of file -- Gitee From c753ae6da12d6d09ab44bcdc460638ffc1eb2593 Mon Sep 17 00:00:00 2001 From: 18356073052 <274131322@qq.com> Date: Sun, 17 Jan 2021 23:06:26 +0800 Subject: [PATCH 031/177] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=BE=A4=E5=8F=91=E6=B6=88=E6=81=AF=E7=BB=93=E6=9E=9C=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeCustomerMessagePushController.java | 12 ++- .../IWeCustomerMessageOriginalService.java | 8 ++ .../WeCustomerMessageOriginalServiceImpl.java | 73 ++++++++++--------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java index 588fba812..1a87118a7 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java @@ -87,10 +87,20 @@ public class WeCustomerMessagePushController extends BaseController { */ @PreAuthorize("@ss.hasPermi('customerMessagePush:push:pushResults')") @GetMapping(value = "/pushResults") - public TableDataInfo customerMessagePushResult(@RequestParam(value = "messageId") Long messageId,@RequestParam(value = "status") String status){ + public TableDataInfo customerMessagePushResult(@RequestParam(value = "messageId") Long messageId, @RequestParam(value = "status") String status) { startPage(); List weCustomerMessageResuls = weCustomerMessgaeResultService.customerMessagePushs(messageId, status); return getDataTable(weCustomerMessageResuls); } + /** + * 同步消息发送结果 + */ + @PreAuthorize("@ss.hasPermi('customerMessagePush:push:asyncResult')") + @PostMapping(value = "asyncResult") + public AjaxResult asyncResult(@RequestParam(value = "msgid") String msgid, @RequestParam(value = "messageId") Long messageId) { + return AjaxResult.success(); + } + + } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeCustomerMessageOriginalService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeCustomerMessageOriginalService.java index 4c94d1c46..a6bce7471 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeCustomerMessageOriginalService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeCustomerMessageOriginalService.java @@ -40,4 +40,12 @@ public interface IWeCustomerMessageOriginalService extends IService{ + CompletableFuture.runAsync(() -> syncSendResult(customerMessagePushDetail.getMsgid(), messageId)); + + return customerMessagePushDetail; + } + + @Override + public void asyncResult(String msgid, Long messageId) { + syncSendResult(msgid, messageId); + } - String msgid = customerMessagePushDetail.getMsgid(); + public void syncSendResult(String msgid, Long messageId) { - if(StringUtils.isNotEmpty(msgid)){ + AtomicInteger atomicInteger = new AtomicInteger(); - List msgIds = null; + if (StringUtils.isNotEmpty(msgid)) { - try { + List msgIds = null; - msgIds = objectMapper.readValue(msgid,new TypeReference>() { }); + try { - } catch (JsonProcessingException e) { + msgIds = objectMapper.readValue(msgid, new TypeReference>() { + }); - e.printStackTrace(); + } catch (JsonProcessingException e) { - } + e.printStackTrace(); - if(CollectionUtils.isNotEmpty(msgIds)){ + } - msgIds.forEach(m->{ + if (CollectionUtils.isNotEmpty(msgIds)) { - QueryCustomerMessageStatusResultDataObjectDto dataObjectDto=new QueryCustomerMessageStatusResultDataObjectDto(); + msgIds.forEach(m -> { - dataObjectDto.setMsgid(m); + QueryCustomerMessageStatusResultDataObjectDto dataObjectDto = new QueryCustomerMessageStatusResultDataObjectDto(); - //拉取发送结果 - QueryCustomerMessageStatusResultDto queryCustomerMessageStatusResultDto = weCustomerMessagePushClient.queryCustomerMessageStatus(dataObjectDto); + dataObjectDto.setMsgid(m); - if (WeConstans.WE_SUCCESS_CODE.equals(queryCustomerMessageStatusResultDto.getErrcode())) { + //拉取发送结果 + QueryCustomerMessageStatusResultDto queryCustomerMessageStatusResultDto = weCustomerMessagePushClient.queryCustomerMessageStatus(dataObjectDto); + if (WeConstans.WE_SUCCESS_CODE.equals(queryCustomerMessageStatusResultDto.getErrcode())) { - List detailList = queryCustomerMessageStatusResultDto.getDetail_list(); - detailList.forEach(d-> { + List detailList = queryCustomerMessageStatusResultDto.getDetail_list(); - if(d.getStatus().equals("1")){ + detailList.forEach(d -> { - atomicInteger.incrementAndGet(); + if (d.getStatus().equals("1")) { - } + atomicInteger.incrementAndGet(); - weCustomerMessgaeResultMapper.updateWeCustomerMessgaeResult(messageId,d.getChat_id(),d.getExternal_userid(),d.getStatus(),d.getSend_time()); + } - }); + weCustomerMessgaeResultMapper.updateWeCustomerMessgaeResult(messageId, d.getChat_id(), d.getExternal_userid(), d.getStatus(), d.getSend_time()); - } + }); - }); + } - } + }); } - //更新微信实际发送条数 - weCustomerMessageService.updateWeCustomerMessageActualSend(messageId,atomicInteger.get()); + } - }); - - return customerMessagePushDetail; + //更新微信实际发送条数 + weCustomerMessageService.updateWeCustomerMessageActualSend(messageId, atomicInteger.get()); } } -- Gitee From 4b3217ef393ff0f6e65231d7c4b16d1bef1417d2 Mon Sep 17 00:00:00 2001 From: 18356073052 <274131322@qq.com> Date: Sun, 17 Jan 2021 23:07:09 +0800 Subject: [PATCH 032/177] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=BE=A4=E5=8F=91=E6=B6=88=E6=81=AF=E7=BB=93=E6=9E=9C=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controller/wecom/WeCustomerMessagePushController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java index 1a87118a7..af86a67a7 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCustomerMessagePushController.java @@ -99,6 +99,7 @@ public class WeCustomerMessagePushController extends BaseController { @PreAuthorize("@ss.hasPermi('customerMessagePush:push:asyncResult')") @PostMapping(value = "asyncResult") public AjaxResult asyncResult(@RequestParam(value = "msgid") String msgid, @RequestParam(value = "messageId") Long messageId) { + weCustomerMessageOriginalService.asyncResult(msgid, messageId); return AjaxResult.success(); } -- Gitee From 33effdb29131297fdc53ee71063a04cb955108bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B4=E5=BA=93=E6=B5=AA=E5=AD=90?= <1539136324@qq.com> Date: Sun, 17 Jan 2021 23:10:17 +0800 Subject: [PATCH 033/177] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E6=8E=A5=E5=8F=A3=E4=B8=BA=E5=8C=BF=E5=90=8D=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/system/SysUserController.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysUserController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysUserController.java index 41c2c59c9..5c982aa01 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysUserController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysUserController.java @@ -1,5 +1,6 @@ package com.linkwechat.web.controller.system; +import cn.hutool.core.collection.CollectionUtil; import com.linkwechat.common.annotation.Log; import com.linkwechat.common.constant.UserConstants; import com.linkwechat.common.core.controller.BaseController; @@ -17,19 +18,23 @@ import com.linkwechat.framework.web.service.TokenService; import com.linkwechat.system.service.ISysPostService; import com.linkwechat.system.service.ISysRoleService; import com.linkwechat.system.service.ISysUserService; +import com.linkwechat.wecom.domain.WeUser; +import com.linkwechat.wecom.service.IWeUserService; import io.swagger.annotations.ApiOperation; +import io.vertx.ext.auth.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.stream.Collectors; /** * 用户信息 - * + * * @author ruoyi */ @RestController @@ -48,6 +53,10 @@ public class SysUserController extends BaseController @Autowired private TokenService tokenService; + + @Autowired + private IWeUserService iWeUserService; + /** * 获取用户列表 */ @@ -56,6 +65,7 @@ public class SysUserController extends BaseController public TableDataInfo list(SysUser user) { startPage(); + List list = userService.selectUserList(user); return getDataTable(list); } @@ -193,4 +203,27 @@ public class SysUserController extends BaseController user.setUpdateBy(SecurityUtils.getUsername()); return toAjax(userService.updateUserStatus(user)); } + + + @GetMapping("/findCurrentLoginUser") + public AjaxResult findCurrentLoginUser(HttpServletRequest request){ + String userId=""; + LoginUser loginUser = tokenService.getLoginUser(request); + if(null != loginUser){ + SysUser user = loginUser.getUser(); + if(null != user){ + List weUsers = iWeUserService.selectWeUserList(WeUser.builder() + .mobile(user.getPhonenumber()) + .build()); + if(CollectionUtil.isNotEmpty(weUsers)){ + userId=weUsers.get(0).getUserId(); + + } + } + + } + return AjaxResult.success(userId); + } + + } -- Gitee From 87360c67a64e94bf31892716596ec0ec5ca2d782 Mon Sep 17 00:00:00 2001 From: xinlaa <1570728529@qq.com> Date: Sun, 17 Jan 2021 23:17:27 +0800 Subject: [PATCH 034/177] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linkwe-mobile/public/index.html | 1 + linkwe-mobile/src/App.vue | 82 ++++++++++++++++++++++---- linkwe-mobile/src/api/chat.js | 7 +-- linkwe-mobile/src/api/common.js | 46 ++------------- linkwe-mobile/src/utils/request.js | 4 +- linkwe-mobile/src/views/chat/List.vue | 73 +++++++++++++++++++++-- linkwe-mobile/src/views/chat/index.vue | 22 +++++-- 7 files changed, 166 insertions(+), 69 deletions(-) diff --git a/linkwe-mobile/public/index.html b/linkwe-mobile/public/index.html index d664e8c8a..a98195aaa 100644 --- a/linkwe-mobile/public/index.html +++ b/linkwe-mobile/public/index.html @@ -8,6 +8,7 @@ <%= htmlWebpackPlugin.options.title %> + diff --git a/linkwe-mobile/src/App.vue b/linkwe-mobile/src/App.vue index 2097698a0..56e423dd8 100644 --- a/linkwe-mobile/src/App.vue +++ b/linkwe-mobile/src/App.vue @@ -4,26 +4,82 @@ export default { name: 'App', data() { return { - timestamp: '', - nonceStr: '', - signature: '', + appId: 'ww38152475d1bca752', + agentId: '1000003', } }, created() { - // 调取服务端signature + // this.wxConfig() }, watch: { // 通过config接口注入权限验证配置 // 所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA(single-page application)的web app可在每次url变化时进行调用) - $router() { - wx.config({ - beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题 - debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 - appId: '123456', // 必填,企业微信的corpID - timestamp: this.timestamp, // 必填,生成签名的时间戳 - nonceStr: this.nonceStr, // 必填,生成签名的随机串 - signature: this.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法 - jsApiList: [], // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来 + $route() { + this.wxConfig() + }, + }, + methods: { + wxConfig() { + getAgentTicket(window.location.href.split('#')[0]).then(({ data }) => { + let { timestamp, nonceStr, signature } = data + wx.agentConfig({ + corpid: this.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致 + agentid: this.agentId, // 必填,企业微信的应用id (e.g. 1000247) + timestamp, // 必填,生成签名的时间戳 + nonceStr, // 必填,生成签名的随机串 + signature, // 必填,签名,见附录-JS-SDK使用权限签名算法 + jsApiList: ['sendChatMessage'], //必填 + success: (res) => { + // 回调 + }, + fail: (res) => { + if (res.errMsg.indexOf('function not exist') > -1) { + alert('版本过低请升级') + } + }, + }) + }) + }, + _wxConfig() { + // 获取企业的jsapi_ticket + getAppTicket(window.location.href).then(({ data }) => { + let { timestamp, nonceStr, signature } = data + wx.config({ + beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题 + debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 + appId: this.appId, // 必填,企业微信的corpID + timestamp, // 必填,生成签名的时间戳 + nonceStr, // 必填,生成签名的随机串 + signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法 + jsApiList: ['getContext', 'sendChatMessage'], // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来 + }) + wx.ready(() => { + // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 + getAgentTicket(window.location.href).then(({ data }) => { + let { timestamp, nonceStr, signature } = data + wx.agentConfig({ + corpid: this.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致 + agentid: this.agentId, // 必填,企业微信的应用id (e.g. 1000247) + timestamp, // 必填,生成签名的时间戳 + nonceStr, // 必填,生成签名的随机串 + signature, // 必填,签名,见附录-JS-SDK使用权限签名算法 + jsApiList: ['sendChatMessage'], //必填 + success: (res) => { + // 回调 + }, + fail: (res) => { + if (res.errMsg.indexOf('function not exist') > -1) { + alert('版本过低请升级') + } + }, + }) + }) + }) + + wx.error(function(res) { + console.error(res) + // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 + }) }) }, }, diff --git a/linkwe-mobile/src/api/chat.js b/linkwe-mobile/src/api/chat.js index 7f035fab1..5cf50bfaa 100644 --- a/linkwe-mobile/src/api/chat.js +++ b/linkwe-mobile/src/api/chat.js @@ -5,12 +5,9 @@ const service = window.CONFIG.services.wecom + '/chat' * 侧边栏列表 * @param {*} params */ -export function getTypeList(params) { +export function getTypeList() { return request({ - url: service + '/side/list', - params: { - h5: 1, - }, + url: service + '/side/h5List', }) } diff --git a/linkwe-mobile/src/api/common.js b/linkwe-mobile/src/api/common.js index 1498c8e97..c5609d7f5 100644 --- a/linkwe-mobile/src/api/common.js +++ b/linkwe-mobile/src/api/common.js @@ -1,5 +1,6 @@ import request from '@/utils/request' -const service = window.CONFIG.services.wecom + '/ticket' +import config from '@/config' +const service = config.services.wecom + '/ticket' /** * 获取应用的jsapi_ticket @@ -28,46 +29,11 @@ export function getAppTicket(url) { } /** - * 收藏列表(h5我的) - * @param {*} userId - */ -export function getCollectionList(userId) { - return request({ - url: service + '/collection/list', - params: { - userId, - }, - }) -} - -/** - * 添加收藏 - * @param {*} data - * { - materialId:素材id -userId:用户id -} - */ -export function addCollection(data) { - return request({ - url: service + '/collection/addCollection', - method: 'post', - data, - }) -} - -/** - * 侧边栏抓取素材 - * @param {*} data - * { - materialId:素材id -userId:用户id -} + * 获取企业的jsapi_ticket + * @param {*} url 页面url */ -export function cancleCollection(data) { +export function getLoginUserId() { return request({ - url: service + '/collection/cancleCollection', - method: 'put', - data, + url: '/system/user/findCurrentLoginUser', }) } diff --git a/linkwe-mobile/src/utils/request.js b/linkwe-mobile/src/utils/request.js index 5956d7f04..b997528d5 100644 --- a/linkwe-mobile/src/utils/request.js +++ b/linkwe-mobile/src/utils/request.js @@ -24,8 +24,8 @@ function createAxios(baseURL) { // console.log('res: ' + res) const { data, status } = res // code 0:成功,-1/其它:错误 - if (status === 200 && data.code === 0) { - return JSON.stringify(data.result) ? data.result : data.data + if (status === 200 && data.code === 200) { + return data } else if (data.code === 201) { // Message.error({ // content: `产品已存在`, diff --git a/linkwe-mobile/src/views/chat/List.vue b/linkwe-mobile/src/views/chat/List.vue index 930ce6e8f..1662046a8 100644 --- a/linkwe-mobile/src/views/chat/List.vue +++ b/linkwe-mobile/src/views/chat/List.vue @@ -32,7 +32,10 @@ export default { methods: { getList() { this.loading = true - ;(this.userId ? getCollectionList : getMaterialList)() + ;(this.sideId + ? getMaterialList(this.sideId) + : getCollectionList(this.userId) + ) .then(({ rows, total }) => { this.list = rows this.loading = false @@ -51,7 +54,67 @@ export default { isCollected(id) { return this.collectList.includes(id) }, - send() {}, + send(data) { + let entry = undefined + wx.invoke('getContext', {}, function(res) { + if (res.err_msg == 'getContext:ok') { + entry = res.entry //返回进入H5页面的入口类型,目前有normal、contact_profile、single_chat_tools、group_chat_tools + if (!['single_chat_tools', 'group_chat_tools'].includes(entry)) { + return + } + // mediaType 0 图片(image)、1 语音(voice)、2 视频(video),3 普通文件(file) 4 文本 5 海报 + // msgtype 文本(“text”),图片(“image”),视频(“video”),文件(“file”),H5(“news”)和小程序(“miniprogram”) + let mes = {} + let msgtype = { + 0: 'image', + 2: 'video', + 3: 'file', + 4: 'text', + 5: 'news', + } + switch (data.mediaType) { + case '4': + default: + mes.text = { + content: data.content, //文本内容 + } + break + case '0': + case '2': + case '3': + mes[msgtype[data.mediaType]] = { + mediaid: data.materialId, // + } + break + case '5': + mes.news = { + link: '', //H5消息页面url 必填 + title: '', //H5消息标题 + desc: '', //H5消息摘要 + imgUrl: '', //H5消息封面图片URL + } + break + // case '6': + // mes.miniprogram = { + // appid: 'wx8bd80126147df384', //小程序的appid + // title: 'this is title', //小程序消息的title + // imgUrl: + // 'https://search-operate.cdn.bcebos.com/d054b8892a7ab572cb296d62ec7f97b6.png', //小程序消息的封面图。必须带http或者https协议头,否则报错 $apiName$:fail invalid imgUrl + // page: '/index/page.html', //小程序消息打开后的路径,注意要以.html作为后缀,否则在微信端打开会提示找不到页面 + // } + // break + } + mes.msgtype = msgtype[data.mediaType] + wx.invoke('sendChatMessage', mes, function(res) { + if (res.err_msg == 'sendChatMessage:ok') { + //发送成功 + } + }) + } else { + //错误处理 + } + }) + }, collect(materialId) { ;(this.isCollected(materialId) ? cancleCollection : addCollection)({ userId, @@ -73,13 +136,13 @@ export default { @load="getList" >
-
dsdsds
+
{{ item.content }}
- 素材库 + {{ item.content }} 2020/2/4
-
发送
+
发送
{{ isCollected(item.id) ? '已' : '' }}收藏
diff --git a/linkwe-mobile/src/views/chat/index.vue b/linkwe-mobile/src/views/chat/index.vue index 77cb04356..d41289bbb 100644 --- a/linkwe-mobile/src/views/chat/index.vue +++ b/linkwe-mobile/src/views/chat/index.vue @@ -1,4 +1,5 @@ + \ No newline at end of file diff --git a/linkwe-ui/src/views/conversation/component/list.vue b/linkwe-ui/src/views/conversation/component/list.vue index aca6430a7..949714544 100644 --- a/linkwe-ui/src/views/conversation/component/list.vue +++ b/linkwe-ui/src/views/conversation/component/list.vue @@ -4,10 +4,10 @@
  • - - + +

    {{item.receiveWeCustomer.name}} {{parseTime(item.finalChatContext.msgtime)}}

    -

    {{item.finalChatContext.text.content}}

    +

    {{item.finalChatContext.text.content}}

  • diff --git a/linkwe-ui/src/views/conversation/page/employeeTest.vue b/linkwe-ui/src/views/conversation/page/employeeTest.vue index 4fcf3540f..7b3c6eb58 100644 --- a/linkwe-ui/src/views/conversation/page/employeeTest.vue +++ b/linkwe-ui/src/views/conversation/page/employeeTest.vue @@ -32,7 +32,8 @@ - + + @@ -151,6 +152,7 @@ \ No newline at end of file + } + + .pagination { + padding: 10px 0; + height: 30px; + + float: left; + } + + -- Gitee From 339e7650b5a14c5f2262050cdcc4644bf849e2bf Mon Sep 17 00:00:00 2001 From: qiyeqing <444251443@qq.com> Date: Thu, 21 Jan 2021 14:07:30 +0800 Subject: [PATCH 046/177] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../conversation/component/customerList.vue | 76 +++++++++++++++++++ .../src/views/conversation/page/userTest.vue | 29 ++++--- 2 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 linkwe-ui/src/views/conversation/component/customerList.vue diff --git a/linkwe-ui/src/views/conversation/component/customerList.vue b/linkwe-ui/src/views/conversation/component/customerList.vue new file mode 100644 index 000000000..c19a5d0c2 --- /dev/null +++ b/linkwe-ui/src/views/conversation/component/customerList.vue @@ -0,0 +1,76 @@ + + + \ No newline at end of file diff --git a/linkwe-ui/src/views/conversation/page/userTest.vue b/linkwe-ui/src/views/conversation/page/userTest.vue index 317b7c589..e5234586e 100644 --- a/linkwe-ui/src/views/conversation/page/userTest.vue +++ b/linkwe-ui/src/views/conversation/page/userTest.vue @@ -32,24 +32,21 @@
-
- +
- +
- + +
- - + +
-
- - +
@@ -163,8 +160,10 @@
\ No newline at end of file + + } + + .pagination { + padding: 10px 0; + height: 30px; + float: left; + } + + -- Gitee From 4229e8dc2debc6793c3f4b76aceb8fdbd946fa53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=96=9C=E6=97=BA?= Date: Sat, 23 Jan 2021 01:35:34 +0800 Subject: [PATCH 050/177] =?UTF-8?q?=E5=9B=9E=E8=B0=83=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wecom/WeCallBackController.java | 3 +- .../WeConversationArchiveController.java | 2 +- .../controller/wecom/WeUserController.java | 6 +- .../src/main/resources/application.yml | 8 +- .../common/config/ElasticSearchConfig.java | 2 +- .../common/config/WeComeConfig.java | 2 + .../common/constant/WeConstans.java | 8 + .../core/domain/ConversationArchiveQuery.java | 2 +- .../domain/elastic/ElasticSearchDataVo.java | 2 +- .../domain/elastic/ElasticSearchEntity.java | 2 +- .../domain/elastic/ElasticSearchQueryVo.java | 2 +- .../core/elasticsearch/ElasticSearch.java | 2 +- .../common/enums/TransferFailReason.java | 55 ++++ .../common/utils/wecom/RSAUtil.java | 2 +- .../java/com/tencent/wework/FinanceUtils.java | 2 +- .../com/linkwechat/quartz/task/RyTask.java | 10 + .../wecom/client/WeCropTagClient.java | 3 +- .../wecom/client/WeMsgAuditClient.java | 2 +- .../wecom/domain/WeCorpAccount.java | 2 + .../com/linkwechat/wecom/domain/WeUser.java | 1 + .../wecom/domain/dto/WeMessagePushDto.java | 12 +- .../dto/customer/ExternalUserDetail.java | 14 +- .../domain/dto/msgaudit/WeMsgAuditDto.java | 2 +- .../domain/dto/tag/WeCropGroupTagDto.java | 1 + .../wecom/domain/dto/tag/WeCropTagDto.java | 1 + .../domain/dto/tag/WeFindCropTagParam.java | 3 + .../wecom/domain/vo/WeMsgAuditVo.java | 2 +- .../wecom/domain/vo/WxCpXmlMessageVO.java | 11 + .../wecom/factory/WeEventStrategy.java | 63 +++++ .../wecom/factory/WeStrategyBeanFactory.java | 28 ++ .../impl/WeEventChangeContactImpl.java | 248 +---------------- .../impl/WeEventChangeExternalChatImpl.java | 27 ++ .../WeEventChangeExternalContactImpl.java | 185 +------------ .../impl/WeEventChangeExternalTagImpl.java | 26 ++ .../WeCallBackAddExternalContactImpl.java | 102 +++++++ .../WeCallBackAddHalfExternalContactImpl.java | 102 +++++++ .../WeCallBackDelExternalContactImpl.java | 31 +++ .../customer/WeCallBackDelFollowUserImpl.java | 71 +++++ .../WeCallBackEditExternalContactImpl.java | 27 ++ .../WeCallBackMsgAuditApprovedImpl.java | 43 +++ .../customer/WeCallbackTransferFailImpl.java | 34 +++ .../WeCallBackCreateGroupImpl.java | 31 +++ .../customergroup/WeCallBackDismissImpl.java | 31 +++ .../WeCallBackUpdateGroupImpl.java | 30 ++ .../impl/party/WeCallBackCreatePartyImpl.java | 31 +++ .../impl/party/WeCallBackDeletePartyImpl.java | 30 ++ .../impl/party/WeCallBackUpdatePartyImpl.java | 31 +++ .../tag/WeCallBackCreateCustomerTagImpl.java | 42 +++ .../tag/WeCallBackDeleteCustomerTagImpl.java | 41 +++ .../tag/WeCallBackUpdateCustomerTagImpl.java | 41 +++ .../impl/tag/WeCallBackUpdateUserTagImpl.java | 83 ++++++ .../impl/user/WeCallBackCreateUserImpl.java | 30 ++ .../impl/user/WeCallBackDeleteUserImpl.java | 31 +++ .../impl/user/WeCallBackUpdateUserImpl.java | 31 +++ .../interceptor/WeAccessTokenInterceptor.java | 2 + .../linkwechat/wecom/mapper/WeTagMapper.java | 4 +- .../linkwechat/wecom/mapper/WeUserMapper.java | 4 +- .../wecom/service/IWeAccessTokenService.java | 2 + .../IWeConversationArchiveService.java | 2 +- .../wecom/service/IWeDepartmentService.java | 2 + .../wecom/service/IWeGroupService.java | 6 + .../wecom/service/IWeTagGroupService.java | 6 + .../wecom/service/IWeTagService.java | 9 +- .../wecom/service/IWeUserService.java | 25 +- .../impl/WeAccessTokenServiceImpl.java | 9 + .../WeConversationArchiveServiceImpl.java | 2 +- .../service/impl/WeCustomerServiceImpl.java | 10 +- .../service/impl/WeDepartmentServiceImpl.java | 9 + .../service/impl/WeGroupServiceImpl.java | 114 +++++++- .../service/impl/WeTagGroupServiceImpl.java | 258 +++++++++++------- .../wecom/service/impl/WeTagServiceImpl.java | 96 ++++++- .../wecom/service/impl/WeUserServiceImpl.java | 36 ++- .../strategy/SendMessageToUserStrategy.java | 17 +- .../mapper/wecom/WeCorpAccountMapper.xml | 9 +- .../resources/mapper/wecom/WeTagMapper.xml | 8 +- .../resources/mapper/wecom/WeUserMapper.xml | 18 +- 76 files changed, 1683 insertions(+), 599 deletions(-) create mode 100644 linkwe-common/src/main/java/com/linkwechat/common/enums/TransferFailReason.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeStrategyBeanFactory.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalTagImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddExternalContactImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddHalfExternalContactImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelExternalContactImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelFollowUserImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackEditExternalContactImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackMsgAuditApprovedImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallbackTransferFailImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackCreateGroupImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackDismissImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackUpdateGroupImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackCreatePartyImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackDeletePartyImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackUpdatePartyImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateUserTagImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackCreateUserImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackDeleteUserImpl.java create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackUpdateUserImpl.java diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCallBackController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCallBackController.java index 88ab9acf9..5453d9792 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCallBackController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeCallBackController.java @@ -1,6 +1,7 @@ package com.linkwechat.web.controller.wecom; import com.alibaba.fastjson.JSONObject; +import com.linkwechat.common.utils.Threads; import com.linkwechat.common.utils.wecom.WxCryptUtil; import com.linkwechat.web.controller.common.CommonController; import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; @@ -47,7 +48,7 @@ public class WeCallBackController extends CommonController { try { WeCallBackEventFactory factory = weEventHandle.factory(wxCpXmlMessage.getEvent()); if (factory !=null){ - factory.eventHandle(wxCpXmlMessage); + Threads.SINGLE_THREAD_POOL.submit(() -> factory.eventHandle(wxCpXmlMessage)); } } catch (Exception e) { e.printStackTrace(); diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeConversationArchiveController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeConversationArchiveController.java index be966d2c8..17369c344 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeConversationArchiveController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeConversationArchiveController.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** - * @author sxw + * @author danmo * @description 会话存档controller * @date 2020/12/19 13:51 **/ diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeUserController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeUserController.java index cc4e23bc5..b8bc9dbb4 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeUserController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeUserController.java @@ -58,10 +58,10 @@ public class WeUserController extends BaseController { * 获取通讯录相关客户详细信息 */ @PreAuthorize("@ss.hasPermi('contacts:organization:view')") - @GetMapping(value = "/{id}") - public AjaxResult getInfo(@PathVariable("id") Long id) + @GetMapping(value = "/{userId}") + public AjaxResult getInfo(@PathVariable("userId") String userId) { - return AjaxResult.success(weUserService.selectWeUserById(id)); + return AjaxResult.success(weUserService.selectWeUserById(userId)); } /** diff --git a/linkwe-admin/src/main/resources/application.yml b/linkwe-admin/src/main/resources/application.yml index 25c1bd210..49b154250 100644 --- a/linkwe-admin/src/main/resources/application.yml +++ b/linkwe-admin/src/main/resources/application.yml @@ -200,8 +200,10 @@ wecome: - /msgaudit/check_single_agree - /msgaudit/check_room_agree - /msgaudit/groupchat/get + needAgentTokenUrl: + - /message/send #企微回调通知配置 callBack: - appIdOrCorpId: - token: - encodingAesKey: \ No newline at end of file + appIdOrCorpId: ww38152475d1bca752 + token: uEyhhuTKfpS + encodingAesKey: diTFQ6B4IW14UN2JplADuhMZsBZHjvuBG24kHFrr4Px \ No newline at end of file diff --git a/linkwe-common/src/main/java/com/linkwechat/common/config/ElasticSearchConfig.java b/linkwe-common/src/main/java/com/linkwechat/common/config/ElasticSearchConfig.java index 09d3c1637..61754cb57 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/config/ElasticSearchConfig.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/config/ElasticSearchConfig.java @@ -11,7 +11,7 @@ import java.util.ArrayList; import java.util.List; /** - * @author sxw + * @author danmo * @description ElasticSearch 配置 * @date 2020/12/4 9:59 **/ diff --git a/linkwe-common/src/main/java/com/linkwechat/common/config/WeComeConfig.java b/linkwe-common/src/main/java/com/linkwechat/common/config/WeComeConfig.java index aeb7fc238..99dcf47ac 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/config/WeComeConfig.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/config/WeComeConfig.java @@ -33,4 +33,6 @@ public class WeComeConfig { /** 会话存档所需token 的url */ private String[] needChatTokenUrl; + /** 应用所需token 的url **/ + private String[] needAgentTokenUrl; } diff --git a/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java b/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java index 594b6c898..a10f12fa8 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java @@ -33,6 +33,10 @@ public class WeConstans { */ public static final String WE_CHAT_ACCESS_TOKEN = "we_chat_access_token"; + /** + * 应用相关token + */ + public static final String WE_AGENT_ACCESS_TOKEN = "we_agent_access_token"; /** * 企业微信接口返回成功code @@ -240,6 +244,10 @@ public class WeConstans { put("batch_job_result", "weEventBatchJobResultImpl"); //外部联系人事件 put("change_external_contact", "weEventChangeExternalContactImpl"); + //客户群事件 + put("change_external_chat", "weEventChangeExternalChatImpl"); + //客户标签事件 + put("change_external_tag", "weEventChangeExternalTagImpl"); } }; diff --git a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/ConversationArchiveQuery.java b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/ConversationArchiveQuery.java index e75307e6b..af9e1d6c7 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/ConversationArchiveQuery.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/ConversationArchiveQuery.java @@ -3,7 +3,7 @@ package com.linkwechat.common.core.domain; import lombok.Data; /** - * @author sxw + * @author danmo * @description 会话存档接口入参实体 * @date 2020/12/29 14:23 **/ diff --git a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchDataVo.java b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchDataVo.java index 58a88113e..62eab96c7 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchDataVo.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchDataVo.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; /** - * @author sxw + * @author danmo * @description http交互Vo对象 * @date 2020/12/9 14:14 **/ diff --git a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchEntity.java b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchEntity.java index 323a976f1..a62bd9089 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchEntity.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchEntity.java @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; import java.util.Map; /** - * @author sxw + * @author danmo * @description * @date 2020/12/9 14:11 **/ diff --git a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchQueryVo.java b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchQueryVo.java index c4d1b437d..959b4f28e 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchQueryVo.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/core/domain/elastic/ElasticSearchQueryVo.java @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; import java.util.Map; /** - * @author sxw + * @author danmo * @description * @date 2020/12/9 14:18 **/ diff --git a/linkwe-common/src/main/java/com/linkwechat/common/core/elasticsearch/ElasticSearch.java b/linkwe-common/src/main/java/com/linkwechat/common/core/elasticsearch/ElasticSearch.java index 523d0738c..7aa5bafd9 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/core/elasticsearch/ElasticSearch.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/core/elasticsearch/ElasticSearch.java @@ -40,7 +40,7 @@ import java.util.Map; import java.util.function.Consumer; /** - * @author sxw + * @author danmo * @description es工具类 * @date 2020/12/9 14:02 **/ diff --git a/linkwe-common/src/main/java/com/linkwechat/common/enums/TransferFailReason.java b/linkwe-common/src/main/java/com/linkwechat/common/enums/TransferFailReason.java new file mode 100644 index 000000000..3df367fca --- /dev/null +++ b/linkwe-common/src/main/java/com/linkwechat/common/enums/TransferFailReason.java @@ -0,0 +1,55 @@ +package com.linkwechat.common.enums; + +import lombok.Getter; + +/** + * @author danmo + * @description 接替失败的原因 + * @date 2021/1/20 0:14 + **/ +@Getter +public enum TransferFailReason { + /** + * 客户拒绝 + */ + CUSTOMER_REFUSED(3,"customer_refused", "客户拒绝"), + + /** + * 接替成员的客户数达到上限 + */ + CUSTOMER_LIMIT_EXCEED(4,"customer_limit_exceed", "接替成员的客户数达到上限"); + + private int num; + + private String name; + + private String reason; + + TransferFailReason(int num, String name, String reason) { + this.num = num; + this.name = name; + this.reason = reason; + } + + public static String getReason(String name){ + TransferFailReason[] values = TransferFailReason.values(); + + for(TransferFailReason transferFailReason:values){ + if(transferFailReason.getName().equals(name)){ + return transferFailReason.getReason(); + } + } + throw new RuntimeException("无对应原因"); + } + + public static int getNum(String name){ + TransferFailReason[] values = TransferFailReason.values(); + + for(TransferFailReason transferFailReason:values){ + if(transferFailReason.getName().equals(name)){ + return transferFailReason.getNum(); + } + } + throw new RuntimeException("无对应类型"); + } +} diff --git a/linkwe-common/src/main/java/com/linkwechat/common/utils/wecom/RSAUtil.java b/linkwe-common/src/main/java/com/linkwechat/common/utils/wecom/RSAUtil.java index 990e842dd..8a4d30c76 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/utils/wecom/RSAUtil.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/utils/wecom/RSAUtil.java @@ -18,7 +18,7 @@ import java.security.spec.RSAPrivateCrtKeySpec; import java.util.Base64; /** - * @author sxw + * @author danmo * @description * @date 2020/12/7 23:37 **/ diff --git a/linkwe-common/src/main/java/com/tencent/wework/FinanceUtils.java b/linkwe-common/src/main/java/com/tencent/wework/FinanceUtils.java index 432cc7313..c2d5ea4d8 100644 --- a/linkwe-common/src/main/java/com/tencent/wework/FinanceUtils.java +++ b/linkwe-common/src/main/java/com/tencent/wework/FinanceUtils.java @@ -21,7 +21,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; /** - * @author sxw + * @author danmo * @description * @date 2020/12/2 16:01 **/ diff --git a/linkwe-quartz/src/main/java/com/linkwechat/quartz/task/RyTask.java b/linkwe-quartz/src/main/java/com/linkwechat/quartz/task/RyTask.java index 57beccf9c..08a3bf88e 100644 --- a/linkwe-quartz/src/main/java/com/linkwechat/quartz/task/RyTask.java +++ b/linkwe-quartz/src/main/java/com/linkwechat/quartz/task/RyTask.java @@ -98,4 +98,14 @@ public class RyTask { }); redisCache.setCacheObject(WeConstans.CONTACT_SEQ_KEY,index); } + + /** + * + * @param corpId 企业id + * @param secret 会话密钥 + */ + public void getPermitUserList(String corpId, String secret){ + log.info("执行有参方法: params:{},{}", corpId, secret); + + } } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeCropTagClient.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeCropTagClient.java index 5df4f8e61..78805e84f 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeCropTagClient.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeCropTagClient.java @@ -1,5 +1,6 @@ package com.linkwechat.wecom.client; +import com.dtflys.forest.annotation.Body; import com.dtflys.forest.annotation.DataObject; import com.dtflys.forest.annotation.Request; import com.linkwechat.wecom.domain.dto.WeResultDto; @@ -40,7 +41,7 @@ public interface WeCropTagClient { @Request(url = "/externalcontact/get_corp_tag_list", type = "POST" ) - WeCropGroupTagListDto getCorpTagListByTagIds(@DataObject WeFindCropTagParam weFindCropTagParam); + WeCropGroupTagListDto getCorpTagListByTagIds(@Body WeFindCropTagParam weFindCropTagParam); /** diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeMsgAuditClient.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeMsgAuditClient.java index f177bb802..01914abb4 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeMsgAuditClient.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/client/WeMsgAuditClient.java @@ -7,7 +7,7 @@ import com.linkwechat.wecom.domain.dto.msgaudit.WeMsgAuditDto; import com.linkwechat.wecom.domain.vo.WeMsgAuditVo; /** - * @author sxw + * @author danmo * @description 会话存档接口 * @date 2020/12/2 16:45 **/ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeCorpAccount.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeCorpAccount.java index f6717b9c0..dbc650832 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeCorpAccount.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeCorpAccount.java @@ -61,6 +61,8 @@ public class WeCorpAccount extends BaseEntity @ApiModelProperty("应用id") private String agentId; + @ApiModelProperty("应用密钥") + private String agentSecret; @ApiModelProperty("服务商密钥") private String providerSecret; diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeUser.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeUser.java index 27b00b202..6318c75fd 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeUser.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeUser.java @@ -111,6 +111,7 @@ public class WeUser private Date dimissionTime; + private String remark; diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/WeMessagePushDto.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/WeMessagePushDto.java index 507ea05fa..38b0954bb 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/WeMessagePushDto.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/WeMessagePushDto.java @@ -13,22 +13,22 @@ import java.util.List; public class WeMessagePushDto { /** - * 指定接收消息的成员,成员ID列表(多个接收者用‘,’分隔,最多支持1000个)。 + * 指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。 * 特殊情况:指定为”@all”,则向该企业应用的全部成员发送 */ - private List touser; + private String touser; /** - * 指定接收消息的部门,部门ID列表,多个接收者用‘,’分隔,最多支持100个。 + * 指定接收消息的部门,部门ID列表,多个接收者用‘|’分隔,最多支持100个。 * 当touser为”@all”时忽略本参数 */ - private List toparty; + private String toparty; /** - * 指定接收消息的标签,标签ID列表,多个接收者用‘,’分隔,最多支持100个。 + * 指定接收消息的标签,标签ID列表,多个接收者用‘|’分隔,最多支持100个。 * 当touser为”@all”时忽略本参数 */ - private List totag; + private String totag; /** * 消息类型,此时固定为:text diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/customer/ExternalUserDetail.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/customer/ExternalUserDetail.java index 771df5380..e88b0cd66 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/customer/ExternalUserDetail.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/customer/ExternalUserDetail.java @@ -29,7 +29,7 @@ public class ExternalUserDetail extends WeResultDto { @Data public static class ExternalContact{ /** 外部联系人userId */ - private String external_userid; + private String externalUserid; /** 外部联系人名称 */ private String name; /** 外部联系人职位 */ @@ -37,9 +37,9 @@ public class ExternalUserDetail extends WeResultDto { /** 外部联系人头像 */ private String avatar; /** 外部联系人所在企业简称 */ - private String corp_name; + private String corpName; /** 外部联系人所在企业全称 */ - private String corp_full_name; + private String corpFullName; /** 外部联系人的类型,1表示该外部联系人是微信用户,2表示该外部联系人是企业微信用户 */ private Integer type; /** 外部联系人性别 0-未知 1-男性 2-女性 */ @@ -60,13 +60,13 @@ public class ExternalUserDetail extends WeResultDto { /**该成员添加此外部联系人的时间*/ private long createtime; /**该成员对此客户备注的企业名称*/ - private String remark_company; + private String remarkCorpName; /**该成员对此客户备注的手机号码*/ - private String[] remark_mobiles; + private String[] remarkMobiles; /**该成员添加此客户的来源*/ - private Integer add_way; + private Integer addWay; /**发起添加的userid,如果成员主动添加,为成员的userid;如果是客户主动添加,则为客户的外部联系人userid;如果是内部成员共享/管理员分配,则为对应的成员/管理员userid*/ - private String oper_userid; + private String operUserid; /** 企业自定义的state参数,用于区分客户具体是通过哪个「联系我」添加,由企业通过创建「联系我」方式指定 */ private String state; /**标签**/ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/msgaudit/WeMsgAuditDto.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/msgaudit/WeMsgAuditDto.java index af637297d..57910636c 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/msgaudit/WeMsgAuditDto.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/msgaudit/WeMsgAuditDto.java @@ -9,7 +9,7 @@ import java.util.Date; import java.util.List; /** - * @author sxw + * @author danmo * @description 会话存档 * @date 2020/12/2 16:47 **/ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropGroupTagDto.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropGroupTagDto.java index 4b91985c1..912941a14 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropGroupTagDto.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropGroupTagDto.java @@ -27,6 +27,7 @@ public class WeCropGroupTagDto { private String group_id; private String group_name; private Integer order; + private Boolean deleted; private Long create_time; private List tag; diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropTagDto.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropTagDto.java index d8050cfa5..f6aa2367a 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropTagDto.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeCropTagDto.java @@ -23,6 +23,7 @@ public class WeCropTagDto { private String id; private String name; private String order; + private Boolean deleted; private Long create_time; public static List transFormto(List weTags){ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeFindCropTagParam.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeFindCropTagParam.java index a99d2f678..57af2e867 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeFindCropTagParam.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/dto/tag/WeFindCropTagParam.java @@ -3,6 +3,8 @@ package com.linkwechat.wecom.domain.dto.tag; import lombok.Builder; import lombok.Data; +import java.util.List; + /** * @description: 获取标签接口参数实体 * @author: HaoN @@ -12,4 +14,5 @@ import lombok.Data; @Builder public class WeFindCropTagParam { private String[] tag_id; + private List group_id; } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WeMsgAuditVo.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WeMsgAuditVo.java index ee48bb952..f1c93041b 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WeMsgAuditVo.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WeMsgAuditVo.java @@ -5,7 +5,7 @@ import lombok.Data; import java.util.List; /** - * @author sxw + * @author danmo * @description 会话存档入参实体 * @date 2020/12/19 12:38 **/ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java index fd457f6c9..1cf52f346 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java @@ -29,6 +29,17 @@ public class WxCpXmlMessageVO extends WxCpXmlMessage { @XStreamAlias("BatchJob") private BatchJob batchJob = new BatchJob(); + @XStreamAlias("FailReason") + @XStreamConverter(value = XStreamCDataConverter.class) + private String failReason; + + @XStreamAlias("TagType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String tagType; + + @XStreamAlias("Id") + @XStreamConverter(value = XStreamCDataConverter.class) + private String id; @Data public static class BatchJob implements Serializable { diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java new file mode 100644 index 000000000..3040e5c3f --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java @@ -0,0 +1,63 @@ +package com.linkwechat.wecom.factory; + +import com.linkwechat.wecom.domain.WeDepartment; +import com.linkwechat.wecom.domain.WeUser; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; + +import java.util.Arrays; + +/** + * @author danmo + * @description 事件类型策略接口 + * @date 2021/1/20 22:00 + **/ +public abstract class WeEventStrategy { + protected final String tag = "tag"; + protected final String tagGroup = "tag_group"; + + public abstract void eventHandle(WxCpXmlMessageVO message); + + //生成成员数据 + public WeUser setWeUserData(WxCpXmlMessageVO message) { + WeUser weUser = WeUser.builder().userId(message.getUserId()) + .email(message.getEmail()) + .name(message.getName()) + .alias(message.getAlias()) + .gender(message.getGender()) + .address(message.getAddress()) + .telephone(message.getTelephone()) + .mobile(message.getMobile()) + .avatarMediaid(message.getAvatar()) + .position(message.getPosition()) + .build(); + if (message.getStatus() != null) { + weUser.setIsActivate(Integer.valueOf(message.getStatus())); + } + if (message.getIsLeaderInDept() != null) { + String[] isLeaderInDeptArr = Arrays.stream(message.getIsLeaderInDept()) + .map(String::valueOf).toArray(String[]::new); + weUser.setIsLeaderInDept(isLeaderInDeptArr); + } + if (message.getDepartments() != null) { + String[] departmentsArr = Arrays.stream(message.getDepartments()) + .map(String::valueOf).toArray(String[]::new); + weUser.setDepartment(departmentsArr); + } + return weUser; + } + + //部门信息 + public WeDepartment setWeDepartMent(WxCpXmlMessageVO message){ + WeDepartment weDepartment = new WeDepartment(); + if (message.getId() != null) { + weDepartment.setId(Long.valueOf(message.getId())); + } + if (message.getName() != null) { + weDepartment.setName(message.getName()); + } + if (message.getParentId() != null) { + weDepartment.setParentId(Long.valueOf(message.getParentId())); + } + return weDepartment; + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeStrategyBeanFactory.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeStrategyBeanFactory.java new file mode 100644 index 000000000..94c11f82b --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeStrategyBeanFactory.java @@ -0,0 +1,28 @@ +package com.linkwechat.wecom.factory; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author danmo + * @description 策略工厂 + * @date 2021/1/20 22:02 + **/ +@Service +public class WeStrategyBeanFactory { + @Autowired + private final Map eventStrategyMap = new ConcurrentHashMap<>(); + + public WeStrategyBeanFactory(Map strategyMap) { + this.eventStrategyMap.clear(); + strategyMap.forEach((k, v) -> this.eventStrategyMap.put(k, v)); + } + + public void getResource(String type, WxCpXmlMessageVO message) { + eventStrategyMap.get(type).eventHandle(message); + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeContactImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeContactImpl.java index dd0bc54bb..911b7e346 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeContactImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeContactImpl.java @@ -1,25 +1,11 @@ package com.linkwechat.wecom.factory.impl; -import com.alibaba.fastjson.JSONObject; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.linkwechat.common.utils.bean.BeanUtils; -import com.linkwechat.wecom.domain.WeDepartment; -import com.linkwechat.wecom.domain.WeFlowerCustomerRel; -import com.linkwechat.wecom.domain.WeFlowerCustomerTagRel; -import com.linkwechat.wecom.domain.WeUser; import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; import com.linkwechat.wecom.factory.WeCallBackEventFactory; -import com.linkwechat.wecom.service.IWeDepartmentService; -import com.linkwechat.wecom.service.IWeFlowerCustomerRelService; -import com.linkwechat.wecom.service.IWeFlowerCustomerTagRelService; -import com.linkwechat.wecom.service.IWeUserService; +import com.linkwechat.wecom.factory.WeStrategyBeanFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import java.util.stream.Collectors; /** * @author danmo @@ -30,240 +16,12 @@ import java.util.stream.Collectors; @Slf4j public class WeEventChangeContactImpl implements WeCallBackEventFactory { @Autowired - private IWeUserService weUserService; - - @Autowired - private IWeDepartmentService weDepartmentService; - - @Autowired - private IWeFlowerCustomerRelService weFlowerCustomerRelService; - - @Autowired - private IWeFlowerCustomerTagRelService weFlowerCustomerTagRelService; + private WeStrategyBeanFactory weStrategyBeanFactory; @Override public void eventHandle(WxCpXmlMessageVO message) { //新增: create_user 更新: update_user 删除:delete_user String changeType = message.getChangeType(); - switch (changeType) { - case "create_user": - this.addUser(message); - break; - case "update_user": - this.updateUser(message); - break; - case "delete_user": - this.delUser(message); - break; - case "create_party": - this.createParty(message); - break; - case "update_party": - this.updateParty(message); - break; - case "delete_party": - this.deleteParty(message); - break; - case "update_tag": - this.updateTag(message); - break; - default: - break; - } - } - - - @Transactional(rollbackFor = Exception.class) - protected void createParty(WxCpXmlMessageVO message) { - try { - WeDepartment weDepartment = new WeDepartment(); - if (message.getId() != null) { - weDepartment.setId(message.getId()); - } - if (message.getName() != null) { - weDepartment.setName(message.getName()); - } - if (message.getParentId() != null) { - weDepartment.setParentId(Long.valueOf(message.getParentId())); - } - weDepartmentService.save(weDepartment); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - @Transactional(rollbackFor = Exception.class) - protected void deleteParty(WxCpXmlMessageVO message) { - try { - if (message.getId() != null) { - weDepartmentService.removeById(message.getId()); - } - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - @Transactional(rollbackFor = Exception.class) - protected void updateParty(WxCpXmlMessageVO message) { - try { - WeDepartment weDepartment = new WeDepartment(); - if (message.getId() != null) { - weDepartment.setId(message.getId()); - } - if (message.getName() != null) { - weDepartment.setName(message.getName()); - } - if (message.getParentId() != null) { - weDepartment.setParentId(Long.valueOf(message.getParentId())); - } - weDepartmentService.saveOrUpdate(weDepartment); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - @Transactional(rollbackFor = Exception.class) - protected void updateTag(WxCpXmlMessageVO message) { - try { - String tagId = message.getTagId(); - //标签中新增的成员userid列表,用逗号分隔 - List addUserItemsList = Arrays.stream(Optional.ofNullable(message.getAddUserItems()) - .orElse("").split(",")).collect(Collectors.toList()); - //标签中删除的成员userid列表,用逗号分隔 - List delUserItemsList = Arrays.stream(Optional.ofNullable(message.getDelUserItems()) - .orElse("").split(",")).collect(Collectors.toList()); - - //标签中新增的成员userid列表,建立关联 - List weFlowerCustomerTagRels = new ArrayList<>(); - LambdaQueryWrapper relLambdaQueryWrapper = new LambdaQueryWrapper<>(); - relLambdaQueryWrapper.in(WeFlowerCustomerRel::getUserId, addUserItemsList); - List flowerCustomerRelList = weFlowerCustomerRelService.list(relLambdaQueryWrapper); - List idList = Optional.ofNullable(flowerCustomerRelList).orElseGet(ArrayList::new) - .stream().map(WeFlowerCustomerRel::getId).collect(Collectors.toList()); - idList.forEach(id -> { - weFlowerCustomerTagRels.add(WeFlowerCustomerTagRel.builder().flowerCustomerRelId(id) - .tagId(tagId) - .build()); - }); - weFlowerCustomerTagRelService.batchInsetWeFlowerCustomerTagRel(weFlowerCustomerTagRels); - - //当前标签对应成员列表 - LambdaQueryWrapper tagRelLambdaQueryWrapper = new LambdaQueryWrapper<>(); - tagRelLambdaQueryWrapper.eq(WeFlowerCustomerTagRel::getTagId, tagId); - List tagRelList = weFlowerCustomerTagRelService.list(tagRelLambdaQueryWrapper); - - List flowerCustomerRelIdList = Optional.ofNullable(tagRelList).orElseGet(ArrayList::new) - .stream().map(WeFlowerCustomerTagRel::getFlowerCustomerRelId).collect(Collectors.toList()); - - if (!flowerCustomerRelIdList.isEmpty()) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.in(WeFlowerCustomerRel::getId, flowerCustomerRelIdList) - .in(WeFlowerCustomerRel::getUserId, delUserItemsList); - List relList = weFlowerCustomerRelService.list(queryWrapper); - List relIdList = Optional.ofNullable(relList).orElseGet(ArrayList::new) - .stream().map(WeFlowerCustomerRel::getId).collect(Collectors.toList()); - //标签中删除的成员userid列表 - LambdaQueryWrapper tagRelQueryWrapper = new LambdaQueryWrapper<>(); - tagRelQueryWrapper.in(WeFlowerCustomerTagRel::getFlowerCustomerRelId, relIdList); - weFlowerCustomerTagRelService.remove(tagRelQueryWrapper); - } - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - /** - * 新增企业成员 - * - * @param message - */ - @Transactional(rollbackFor = Exception.class) - protected void addUser(WxCpXmlMessageVO message) { - try { - WeUser weUser = WeUser.builder().userId(message.getUserId()) - .email(message.getEmail()) - .name(message.getName()) - .alias(message.getAlias()) - .gender(message.getGender()) - .address(message.getAddress()) - .telephone(message.getTelephone()) - .mobile(message.getMobile()) - .avatarMediaid(message.getAvatar()) - .position(message.getPosition()) - .joinTime(new Date()) - .build(); - setWeUserData(message, weUser); - weUserService.insertWeUser(weUser); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - /** - * 删除企业成员 - * - * @param message - */ - @Transactional(rollbackFor = Exception.class) - protected void delUser(WxCpXmlMessageVO message) { - try { - if (message.getUserId() != null) { - String[] userIdArr = message.getUserId().split(""); - weUserService.deleteUser(userIdArr); - } - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - /** - * 修改成员信息 - * - * @param message - */ - @Transactional(rollbackFor = Exception.class) - protected void updateUser(WxCpXmlMessageVO message) { - try { - - WeUser weUser = WeUser.builder().userId(message.getUserId()) - .email(message.getEmail()) - .name(message.getName()) - .alias(message.getAlias()) - .gender(message.getGender()) - .address(message.getAddress()) - .telephone(message.getTelephone()) - .mobile(message.getMobile()) - .avatarMediaid(message.getAvatar()) - .position(message.getPosition()) - .build(); - setWeUserData(message, weUser); - weUserService.updateWeUser(weUser); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - } - - private void setWeUserData(WxCpXmlMessageVO message, WeUser weUser) { - if (message.getStatus() != null) { - weUser.setIsActivate(Integer.valueOf(message.getStatus())); - } - if (message.getIsLeaderInDept() != null) { - String[] isLeaderInDeptArr = Arrays.stream(message.getIsLeaderInDept()) - .map(String::valueOf).toArray(String[]::new); - weUser.setIsLeaderInDept(isLeaderInDeptArr); - } - - if (message.getDepartments() != null) { - String[] departmentsArr = Arrays.stream(message.getDepartments()) - .map(String::valueOf).toArray(String[]::new); - weUser.setDepartment(departmentsArr); - } + weStrategyBeanFactory.getResource(changeType, message); } } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java new file mode 100644 index 000000000..209e46ab7 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java @@ -0,0 +1,27 @@ +package com.linkwechat.wecom.factory.impl; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeCallBackEventFactory; +import com.linkwechat.wecom.factory.WeStrategyBeanFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author danmo + * @description 客户群事件 + * @date 2021/1/20 1:15 + **/ + +@Service +@Slf4j +public class WeEventChangeExternalChatImpl implements WeCallBackEventFactory { + @Autowired + private WeStrategyBeanFactory weStrategyBeanFactory; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + String changeType = message.getChangeType(); + weStrategyBeanFactory.getResource(changeType,message); + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalContactImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalContactImpl.java index a9b115a04..156ccb1c5 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalContactImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalContactImpl.java @@ -16,6 +16,7 @@ import com.linkwechat.wecom.domain.dto.WeWelcomeMsg; import com.linkwechat.wecom.domain.dto.message.TextMessageDto; import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; import com.linkwechat.wecom.factory.WeCallBackEventFactory; +import com.linkwechat.wecom.factory.WeStrategyBeanFactory; import com.linkwechat.wecom.service.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -33,191 +34,11 @@ import java.util.stream.Collectors; @Slf4j public class WeEventChangeExternalContactImpl implements WeCallBackEventFactory { @Autowired - private IWeEmpleCodeService weEmpleCodeService; - @Autowired - private IWeEmpleCodeTagService weEmpleCodeTagService; - @Autowired - private IWeCustomerService weCustomerService; - @Autowired - private IWeFlowerCustomerRelService weFlowerCustomerRelService; - @Autowired - private IWeFlowerCustomerTagRelService weFlowerCustomerTagRelService; - @Autowired - private IWeMaterialService weMaterialService; - @Autowired - private WeMessagePushClient weMessagePushClient; - @Autowired - private IWeCorpAccountService weCorpAccountService; - @Autowired - private IWeChatContactMappingService weChatContactMappingService; - + private WeStrategyBeanFactory weStrategyBeanFactory; @Override public void eventHandle(WxCpXmlMessageVO message) { String changeType = message.getChangeType(); - switch (changeType) { - case "add_external_contact"://添加企业客户事件 - addExternalContact(message); - break; - case "edit_external_contact"://编辑企业客户事件 - editExternalContact(message); - break; - case "add_half_external_contact"://外部联系人免验证添加成员事件 - addHalfExternalContact(message); - break; - case "del_external_contact"://删除企业客户事件 - delExternalContact(message); - break; - case "del_follow_user"://删除跟进成员事件 - delFollowUser(message); - break; - case "transfer_fail"://客户接替失败事件 - transferFail(message); - break; - case "msg_audit_approved"://客户同意进行聊天内容存档事件 - msgAuditApproved(message); - break; - default: - break; - } - String chatId = message.getChatId(); - if (StringUtils.isNotEmpty(chatId)) { - //客户群变更事件 - weChatChangeEvent(message); - } - } - - private void msgAuditApproved(WxCpXmlMessageVO message) { - String userId = message.getUserId(); - String externalUserId = message.getExternalUserId(); - WeChatContactMapping fromMapping = new WeChatContactMapping(); - fromMapping.setFromId(userId); - fromMapping.setReceiveId(externalUserId); - fromMapping.setIsCustom(WeConstans.ID_TYPE_EX); - weChatContactMappingService.insertWeChatContactMapping(fromMapping); - WeChatContactMapping receiveMapping = new WeChatContactMapping(); - receiveMapping.setFromId(externalUserId); - receiveMapping.setReceiveId(userId); - receiveMapping.setIsCustom(WeConstans.ID_TYPE_USER); - weChatContactMappingService.insertWeChatContactMapping(receiveMapping); - - weCustomerService.updateCustomerChatStatus(externalUserId); - } - - private void weChatChangeEvent(WxCpXmlMessageVO message) { - } - - private void transferFail(WxCpXmlMessageVO message) { - } - - private void delFollowUser(WxCpXmlMessageVO message) { - Threads.SINGLE_THREAD_POOL.execute(new Runnable() { - @Override - public void run() { - if (message.getUserId() != null && message.getExternalUserId() != null) { - weFlowerCustomerRelService.deleteFollowUser(message.getUserId(), message.getExternalUserId()); - WeCorpAccount validWeCorpAccount = weCorpAccountService.findValidWeCorpAccount(); - Optional.ofNullable(validWeCorpAccount).ifPresent(weCorpAccount -> { - String customerChurnNoticeSwitch = weCorpAccount.getCustomerChurnNoticeSwitch(); - if (WeConstans.DEL_FOLLOW_USER_SWITCH_OPEN.equals(customerChurnNoticeSwitch)){ - WeCustomer weCustomer = weCustomerService.getById(message.getExternalUserId()); - String content = "您已经被客户@"+weCustomer.getName()+"删除!" ; - TextMessageDto textMessageDto = new TextMessageDto(); - textMessageDto.setContent(content); - List userIdList = Arrays.stream(message.getUserId().split(",")).collect(Collectors.toList()); - WeMessagePushDto weMessagePushDto = new WeMessagePushDto(); - weMessagePushDto.setMsgtype(MessageType.TEXT.getMessageType()); - weMessagePushDto.setTouser(userIdList); - weMessagePushDto.setText(textMessageDto); - Optional.ofNullable(validWeCorpAccount).map(WeCorpAccount::getAgentId).ifPresent(agentId -> { - weMessagePushDto.setAgentid(Integer.valueOf(agentId)); - }); - weMessagePushClient.sendMessageToUser(weMessagePushDto); - } - }); - } - } - }); - } - - private void delExternalContact(WxCpXmlMessageVO message) { - if (message.getExternalUserId() != null) { - weCustomerService.deleteCustomersByEid(message.getExternalUserId()); - } - } - - private void addHalfExternalContact(WxCpXmlMessageVO message) { - if (message.getExternalUserId() != null) { - weCustomerService.getCustomersInfoAndSynchWeCustomer(message.getExternalUserId()); - } - } - - private void editExternalContact(WxCpXmlMessageVO message) { - if (message.getExternalUserId() != null) { - weCustomerService.getCustomersInfoAndSynchWeCustomer(message.getExternalUserId()); - } - } - - private void addExternalContact(WxCpXmlMessageVO message) { - - try { - Threads.SINGLE_THREAD_POOL.submit(new Runnable() { - @Override - public void run() { - if (message.getExternalUserId() != null) { - weCustomerService.getCustomersInfoAndSynchWeCustomer(message.getExternalUserId()); - } - - //向扫码客户发送欢迎语 - if (message.getState() != null && message.getWelcomeCode() != null) { - log.info("执行发送欢迎语>>>>>>>>>>>>>>>"); - WeWelcomeMsg.WeWelcomeMsgBuilder weWelcomeMsgBuilder = WeWelcomeMsg.builder().welcome_code(message.getWelcomeCode()); - WeEmpleCodeDto messageMap = weEmpleCodeService.selectWelcomeMsgByActivityScene(message.getState(),message.getUserId()); - String empleCodeId = messageMap.getEmpleCodeId(); - //查询活码对应标签 - List tagList = weEmpleCodeTagService.list(new LambdaQueryWrapper() - .eq(WeEmpleCodeTag::getEmpleCodeId, empleCodeId)); - //查询外部联系人与通讯录关系数据 - WeFlowerCustomerRel weFlowerCustomerRel = weFlowerCustomerRelService.getOne(new LambdaQueryWrapper() - .eq(WeFlowerCustomerRel::getUserId, message.getUserId()) - .eq(WeFlowerCustomerRel::getExternalUserid, message.getExternalUserId())); - //为外部联系人添加员工活码标签 - List weFlowerCustomerTagRels = new ArrayList<>(); - Optional.ofNullable(weFlowerCustomerRel).ifPresent(weFlowerCustomerRel1 -> { - Optional.ofNullable(tagList).orElseGet(ArrayList::new).forEach(tag ->{ - weFlowerCustomerTagRels.add( - WeFlowerCustomerTagRel.builder() - .flowerCustomerRelId(weFlowerCustomerRel.getId()) - .tagId(tag.getTagId()) - .createTime(new Date()) - .build() - ); - }); - weFlowerCustomerTagRelService.saveOrUpdateBatch(weFlowerCustomerTagRels); - }); - log.debug(">>>>>>>>>欢迎语查询结果:{}", JSONObject.toJSONString(messageMap)); - if (messageMap != null) { - if (StringUtils.isNotEmpty(messageMap.getWelcomeMsg())){ - weWelcomeMsgBuilder.text(WeWelcomeMsg.Text.builder() - .content(messageMap.getWelcomeMsg()).build()); - } - if(StringUtils.isNotEmpty(messageMap.getCategoryId())){ - WeMediaDto weMediaDto = weMaterialService - .uploadTemporaryMaterial(messageMap.getMaterialUrl(),messageMap.getMaterialName(), MediaType.IMAGE.getMediaType()); - Optional.ofNullable(weMediaDto).ifPresent(media ->{ - weWelcomeMsgBuilder.image(WeWelcomeMsg.Image.builder().media_id(media.getMedia_id()) - .pic_url(media.getUrl()).build()); - }); - - } - weCustomerService.sendWelcomeMsg(weWelcomeMsgBuilder.build()); - } - } - } - }); - } catch (Exception e) { - e.printStackTrace(); - log.error("执行发送欢迎语失败!",e); - } + weStrategyBeanFactory.getResource(changeType,message); } } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalTagImpl.java new file mode 100644 index 000000000..154417117 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalTagImpl.java @@ -0,0 +1,26 @@ +package com.linkwechat.wecom.factory.impl; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeCallBackEventFactory; +import com.linkwechat.wecom.factory.WeStrategyBeanFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author danmo + * @description 客户标签事件 + * @date 2021/1/20 1:13 + **/ +@Service +@Slf4j +public class WeEventChangeExternalTagImpl implements WeCallBackEventFactory { + @Autowired + private WeStrategyBeanFactory weStrategyBeanFactory; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + String changeType = message.getChangeType()+"CustomerTag"; + weStrategyBeanFactory.getResource(changeType,message); + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddExternalContactImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddExternalContactImpl.java new file mode 100644 index 000000000..d6ad4bbfb --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddExternalContactImpl.java @@ -0,0 +1,102 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.linkwechat.common.enums.MediaType; +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.common.utils.Threads; +import com.linkwechat.wecom.domain.WeEmpleCodeTag; +import com.linkwechat.wecom.domain.WeFlowerCustomerRel; +import com.linkwechat.wecom.domain.WeFlowerCustomerTagRel; +import com.linkwechat.wecom.domain.dto.WeEmpleCodeDto; +import com.linkwechat.wecom.domain.dto.WeMediaDto; +import com.linkwechat.wecom.domain.dto.WeWelcomeMsg; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +/** + * @author danmo + * @description 新增客户事件 + * @date 2021/1/20 23:18 + **/ +@Slf4j +@Component("add_external_contact") +public class WeCallBackAddExternalContactImpl extends WeEventStrategy { + @Autowired + private IWeCustomerService weCustomerService; + @Autowired + private IWeEmpleCodeTagService weEmpleCodeTagService; + @Autowired + private IWeEmpleCodeService weEmpleCodeService; + @Autowired + private IWeFlowerCustomerRelService weFlowerCustomerRelService; + @Autowired + private IWeFlowerCustomerTagRelService weFlowerCustomerTagRelService; + @Autowired + private IWeMaterialService weMaterialService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + if (message.getExternalUserId() != null) { + weCustomerService.getCustomersInfoAndSynchWeCustomer(message.getExternalUserId()); + } + //向扫码客户发送欢迎语 + if (message.getState() != null && message.getWelcomeCode() != null) { + log.info("执行发送欢迎语>>>>>>>>>>>>>>>"); + WeWelcomeMsg.WeWelcomeMsgBuilder weWelcomeMsgBuilder = WeWelcomeMsg.builder().welcome_code(message.getWelcomeCode()); + WeEmpleCodeDto messageMap = weEmpleCodeService.selectWelcomeMsgByActivityScene(message.getState(),message.getUserId()); + String empleCodeId = messageMap.getEmpleCodeId(); + //查询活码对应标签 + List tagList = weEmpleCodeTagService.list(new LambdaQueryWrapper() + .eq(WeEmpleCodeTag::getEmpleCodeId, empleCodeId)); + //查询外部联系人与通讯录关系数据 + WeFlowerCustomerRel weFlowerCustomerRel = weFlowerCustomerRelService.getOne(new LambdaQueryWrapper() + .eq(WeFlowerCustomerRel::getUserId, message.getUserId()) + .eq(WeFlowerCustomerRel::getExternalUserid, message.getExternalUserId())); + //为外部联系人添加员工活码标签 + List weFlowerCustomerTagRels = new ArrayList<>(); + Optional.ofNullable(weFlowerCustomerRel).ifPresent(weFlowerCustomerRel1 -> { + Optional.ofNullable(tagList).orElseGet(ArrayList::new).forEach(tag ->{ + weFlowerCustomerTagRels.add( + WeFlowerCustomerTagRel.builder() + .flowerCustomerRelId(weFlowerCustomerRel.getId()) + .tagId(tag.getTagId()) + .createTime(new Date()) + .build() + ); + }); + weFlowerCustomerTagRelService.saveOrUpdateBatch(weFlowerCustomerTagRels); + }); + log.debug(">>>>>>>>>欢迎语查询结果:{}", JSONObject.toJSONString(messageMap)); + if (messageMap != null) { + if (StringUtils.isNotEmpty(messageMap.getWelcomeMsg())){ + weWelcomeMsgBuilder.text(WeWelcomeMsg.Text.builder() + .content(messageMap.getWelcomeMsg()).build()); + } + if(StringUtils.isNotEmpty(messageMap.getCategoryId())){ + WeMediaDto weMediaDto = weMaterialService + .uploadTemporaryMaterial(messageMap.getMaterialUrl(),messageMap.getMaterialName(), MediaType.IMAGE.getMediaType()); + Optional.ofNullable(weMediaDto).ifPresent(media ->{ + weWelcomeMsgBuilder.image(WeWelcomeMsg.Image.builder().media_id(media.getMedia_id()) + .pic_url(media.getUrl()).build()); + }); + } + weCustomerService.sendWelcomeMsg(weWelcomeMsgBuilder.build()); + } + } + } catch (Exception e) { + e.printStackTrace(); + log.error("执行发送欢迎语失败!",e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddHalfExternalContactImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddHalfExternalContactImpl.java new file mode 100644 index 000000000..4f0c4dd1c --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackAddHalfExternalContactImpl.java @@ -0,0 +1,102 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.linkwechat.common.enums.MediaType; +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.wecom.domain.WeEmpleCodeTag; +import com.linkwechat.wecom.domain.WeFlowerCustomerRel; +import com.linkwechat.wecom.domain.WeFlowerCustomerTagRel; +import com.linkwechat.wecom.domain.dto.WeEmpleCodeDto; +import com.linkwechat.wecom.domain.dto.WeMediaDto; +import com.linkwechat.wecom.domain.dto.WeWelcomeMsg; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +/** + * @author danmo + * @description 外部联系人免验证添加成员事件 + * @date 2021/1/20 23:28 + **/ +@Slf4j +@Component("add_half_external_contact") +public class WeCallBackAddHalfExternalContactImpl extends WeEventStrategy { + + @Autowired + private IWeCustomerService weCustomerService; + @Autowired + private IWeEmpleCodeTagService weEmpleCodeTagService; + @Autowired + private IWeEmpleCodeService weEmpleCodeService; + @Autowired + private IWeFlowerCustomerRelService weFlowerCustomerRelService; + @Autowired + private IWeFlowerCustomerTagRelService weFlowerCustomerTagRelService; + @Autowired + private IWeMaterialService weMaterialService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + if (message.getExternalUserId() != null) { + weCustomerService.getCustomersInfoAndSynchWeCustomer(message.getExternalUserId()); + } + //向扫码客户发送欢迎语 + if (message.getState() != null && message.getWelcomeCode() != null) { + log.info("执行发送欢迎语>>>>>>>>>>>>>>>"); + WeWelcomeMsg.WeWelcomeMsgBuilder weWelcomeMsgBuilder = WeWelcomeMsg.builder().welcome_code(message.getWelcomeCode()); + WeEmpleCodeDto messageMap = weEmpleCodeService.selectWelcomeMsgByActivityScene(message.getState(),message.getUserId()); + String empleCodeId = messageMap.getEmpleCodeId(); + //查询活码对应标签 + List tagList = weEmpleCodeTagService.list(new LambdaQueryWrapper() + .eq(WeEmpleCodeTag::getEmpleCodeId, empleCodeId)); + //查询外部联系人与通讯录关系数据 + WeFlowerCustomerRel weFlowerCustomerRel = weFlowerCustomerRelService.getOne(new LambdaQueryWrapper() + .eq(WeFlowerCustomerRel::getUserId, message.getUserId()) + .eq(WeFlowerCustomerRel::getExternalUserid, message.getExternalUserId())); + //为外部联系人添加员工活码标签 + List weFlowerCustomerTagRels = new ArrayList<>(); + Optional.ofNullable(weFlowerCustomerRel).ifPresent(weFlowerCustomerRel1 -> { + Optional.ofNullable(tagList).orElseGet(ArrayList::new).forEach(tag ->{ + weFlowerCustomerTagRels.add( + WeFlowerCustomerTagRel.builder() + .flowerCustomerRelId(weFlowerCustomerRel.getId()) + .tagId(tag.getTagId()) + .createTime(new Date()) + .build() + ); + }); + weFlowerCustomerTagRelService.saveOrUpdateBatch(weFlowerCustomerTagRels); + }); + log.debug(">>>>>>>>>欢迎语查询结果:{}", JSONObject.toJSONString(messageMap)); + if (messageMap != null) { + if (StringUtils.isNotEmpty(messageMap.getWelcomeMsg())){ + weWelcomeMsgBuilder.text(WeWelcomeMsg.Text.builder() + .content(messageMap.getWelcomeMsg()).build()); + } + if(StringUtils.isNotEmpty(messageMap.getCategoryId())){ + WeMediaDto weMediaDto = weMaterialService + .uploadTemporaryMaterial(messageMap.getMaterialUrl(),messageMap.getMaterialName(), MediaType.IMAGE.getMediaType()); + Optional.ofNullable(weMediaDto).ifPresent(media ->{ + weWelcomeMsgBuilder.image(WeWelcomeMsg.Image.builder().media_id(media.getMedia_id()) + .pic_url(media.getUrl()).build()); + }); + } + weCustomerService.sendWelcomeMsg(weWelcomeMsgBuilder.build()); + } + } + } catch (Exception e) { + e.printStackTrace(); + log.error("执行发送欢迎语失败!",e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelExternalContactImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelExternalContactImpl.java new file mode 100644 index 000000000..da16ecd2e --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelExternalContactImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeCustomerService; +import com.linkwechat.wecom.service.IWeFlowerCustomerRelService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 删除企业客户事件 + * @date 2021/1/20 23:33 + **/ +@Slf4j +@Component("del_external_contact") +public class WeCallBackDelExternalContactImpl extends WeEventStrategy { + @Autowired + private IWeCustomerService weCustomerService; + @Autowired + private IWeFlowerCustomerRelService weFlowerCustomerRelService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + if (message.getExternalUserId() != null) { + weCustomerService.deleteCustomersByEid(message.getExternalUserId()); + weFlowerCustomerRelService.deleteFollowUser(message.getUserId(),message.getExternalUserId()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelFollowUserImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelFollowUserImpl.java new file mode 100644 index 000000000..f02945b87 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackDelFollowUserImpl.java @@ -0,0 +1,71 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.alibaba.fastjson.JSONObject; +import com.linkwechat.common.constant.WeConstans; +import com.linkwechat.common.enums.MessageType; +import com.linkwechat.wecom.client.WeMessagePushClient; +import com.linkwechat.wecom.domain.WeCorpAccount; +import com.linkwechat.wecom.domain.WeCustomer; +import com.linkwechat.wecom.domain.dto.WeMessagePushDto; +import com.linkwechat.wecom.domain.dto.message.TextMessageDto; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeCorpAccountService; +import com.linkwechat.wecom.service.IWeCustomerService; +import com.linkwechat.wecom.service.IWeFlowerCustomerRelService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author danmo + * @description 删除跟进成员事件 + * @date 2021/1/20 23:36 + **/ +@Slf4j +@Component("del_follow_user") +public class WeCallBackDelFollowUserImpl extends WeEventStrategy { + @Autowired + private IWeCustomerService weCustomerService; + @Autowired + private IWeFlowerCustomerRelService weFlowerCustomerRelService; + @Autowired + private IWeCorpAccountService weCorpAccountService; + @Autowired + private WeMessagePushClient weMessagePushClient; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + if (message.getUserId() != null && message.getExternalUserId() != null) { + weFlowerCustomerRelService.deleteFollowUser(message.getUserId(), message.getExternalUserId()); + WeCorpAccount validWeCorpAccount = weCorpAccountService.findValidWeCorpAccount(); + Optional.ofNullable(validWeCorpAccount).ifPresent(weCorpAccount -> { + String customerChurnNoticeSwitch = weCorpAccount.getCustomerChurnNoticeSwitch(); + if (WeConstans.DEL_FOLLOW_USER_SWITCH_OPEN.equals(customerChurnNoticeSwitch)){ + WeCustomer weCustomer = weCustomerService.selectWeCustomerById(message.getExternalUserId()); + String content = "您已经被客户@"+weCustomer.getName()+"删除!" ; + TextMessageDto textMessageDto = new TextMessageDto(); + textMessageDto.setContent(content); + WeMessagePushDto weMessagePushDto = new WeMessagePushDto(); + weMessagePushDto.setMsgtype(MessageType.TEXT.getMessageType()); + weMessagePushDto.setTouser(message.getUserId()); + weMessagePushDto.setText(textMessageDto); + Optional.ofNullable(validWeCorpAccount).map(WeCorpAccount::getAgentId).ifPresent(agentId -> { + weMessagePushDto.setAgentid(Integer.valueOf(agentId)); + }); + weMessagePushClient.sendMessageToUser(weMessagePushDto); + } + }); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("del_follow_user>>>>>>>>>>>>>param:{},ex:{}", JSONObject.toJSONString(message),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackEditExternalContactImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackEditExternalContactImpl.java new file mode 100644 index 000000000..ef1c1920c --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackEditExternalContactImpl.java @@ -0,0 +1,27 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeCustomerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 编辑客户事件 + * @date 2021/1/20 23:25 + **/ +@Slf4j +@Component("edit_external_contact") +public class WeCallBackEditExternalContactImpl extends WeEventStrategy { + @Autowired + private IWeCustomerService weCustomerService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + if (message.getExternalUserId() != null) { + weCustomerService.getCustomersInfoAndSynchWeCustomer(message.getExternalUserId()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackMsgAuditApprovedImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackMsgAuditApprovedImpl.java new file mode 100644 index 000000000..0ed6884fb --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallBackMsgAuditApprovedImpl.java @@ -0,0 +1,43 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.linkwechat.common.constant.WeConstans; +import com.linkwechat.wecom.client.WeMessagePushClient; +import com.linkwechat.wecom.domain.WeChatContactMapping; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 客户同意进行聊天内容存档事件回调 + * @date 2021/1/21 1:24 + **/ +@Slf4j +@Component("msg_audit_approved") +public class WeCallBackMsgAuditApprovedImpl extends WeEventStrategy { + @Autowired + private IWeCustomerService weCustomerService; + @Autowired + private IWeChatContactMappingService weChatContactMappingService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + String userId = message.getUserId(); + String externalUserId = message.getExternalUserId(); + WeChatContactMapping fromMapping = new WeChatContactMapping(); + fromMapping.setFromId(userId); + fromMapping.setReceiveId(externalUserId); + fromMapping.setIsCustom(WeConstans.ID_TYPE_EX); + weChatContactMappingService.insertWeChatContactMapping(fromMapping); + WeChatContactMapping receiveMapping = new WeChatContactMapping(); + receiveMapping.setFromId(externalUserId); + receiveMapping.setReceiveId(userId); + receiveMapping.setIsCustom(WeConstans.ID_TYPE_USER); + weChatContactMappingService.insertWeChatContactMapping(receiveMapping); + + weCustomerService.updateCustomerChatStatus(externalUserId); + } +} \ No newline at end of file diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallbackTransferFailImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallbackTransferFailImpl.java new file mode 100644 index 000000000..bd3efd712 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customer/WeCallbackTransferFailImpl.java @@ -0,0 +1,34 @@ +package com.linkwechat.wecom.factory.impl.customer; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.linkwechat.common.enums.TransferFailReason; +import com.linkwechat.wecom.domain.WeAllocateCustomer; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeAllocateCustomerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 客户接替失败事件 + * @date 2021/1/20 23:41 + **/ +@Slf4j +@Component("transfer_fail") +public class WeCallbackTransferFailImpl extends WeEventStrategy { + @Autowired + private IWeAllocateCustomerService iWeAllocateCustomerService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + String userId = message.getUserId(); + String externalUserId = message.getExternalUserId(); + String failReason = message.getFailReason(); + WeAllocateCustomer weAllocateCustomer = WeAllocateCustomer.builder().failReason(TransferFailReason.getReason(failReason)) + .status(TransferFailReason.getNum(failReason)).build(); + iWeAllocateCustomerService.update(weAllocateCustomer,new LambdaQueryWrapper().eq(WeAllocateCustomer::getTakeoverUserid,userId) + .eq(WeAllocateCustomer::getExternalUserid,externalUserId)); + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackCreateGroupImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackCreateGroupImpl.java new file mode 100644 index 000000000..1275975de --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackCreateGroupImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.customergroup; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeGroupService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 客户群创建事件 + * @date 2021/1/20 0:28 + **/ +@Slf4j +@Component("create") +public class WeCallBackCreateGroupImpl extends WeEventStrategy { + + @Autowired + private IWeGroupService weGroupService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weGroupService.createWeGroup(message.getChatId()); + } catch (Exception e) { + e.printStackTrace(); + log.error("create>>>>>>>>>param:{},ex:{}",message.getChatId(),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackDismissImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackDismissImpl.java new file mode 100644 index 000000000..0c8890048 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackDismissImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.customergroup; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeGroupService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 客户群解散事件 + * @date 2021/1/20 1:07 + **/ +@Slf4j +@Component("dismiss") +public class WeCallBackDismissImpl extends WeEventStrategy { + + @Autowired + private IWeGroupService weGroupService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weGroupService.deleteWeGroup(message.getChatId()); + } catch (Exception e) { + e.printStackTrace(); + log.error("dismiss>>>>>>>>>param:{},ex:{}",message.getChatId(),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackUpdateGroupImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackUpdateGroupImpl.java new file mode 100644 index 000000000..c8458067d --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/customergroup/WeCallBackUpdateGroupImpl.java @@ -0,0 +1,30 @@ +package com.linkwechat.wecom.factory.impl.customergroup; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeGroupService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 客户群变更事件 + * @date 2021/1/20 0:39 + **/ +@Slf4j +@Component("update") +public class WeCallBackUpdateGroupImpl extends WeEventStrategy { + @Autowired + private IWeGroupService weGroupService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weGroupService.updateWeGroup(message.getChatId()); + } catch (Exception e) { + e.printStackTrace(); + log.error("update>>>>>>>>>param:{},ex:{}",message.getChatId(),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackCreatePartyImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackCreatePartyImpl.java new file mode 100644 index 000000000..9137a247e --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackCreatePartyImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.party; + +import com.linkwechat.wecom.domain.WeDepartment; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeDepartmentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 创建部门事件 + * @date 2021/1/20 22:54 + **/ +@Slf4j +@Component("create_party") +public class WeCallBackCreatePartyImpl extends WeEventStrategy { + @Autowired + private IWeDepartmentService weDepartmentService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weDepartmentService.insertWeDepartmentNoToWeCom(setWeDepartMent(message)); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackDeletePartyImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackDeletePartyImpl.java new file mode 100644 index 000000000..444464cc6 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackDeletePartyImpl.java @@ -0,0 +1,30 @@ +package com.linkwechat.wecom.factory.impl.party; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeDepartmentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 删除部门事件 + * @date 2021/1/20 23:04 + **/ +@Slf4j +@Component("delete_party") +public class WeCallBackDeletePartyImpl extends WeEventStrategy { + @Autowired + private IWeDepartmentService weDepartmentService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weDepartmentService.removeById(message.getId()); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackUpdatePartyImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackUpdatePartyImpl.java new file mode 100644 index 000000000..d927cce06 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/party/WeCallBackUpdatePartyImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.party; + +import com.linkwechat.wecom.domain.WeDepartment; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeDepartmentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 修改部门事件 + * @date 2021/1/20 23:00 + **/ +@Slf4j +@Component("update_party") +public class WeCallBackUpdatePartyImpl extends WeEventStrategy { + @Autowired + private IWeDepartmentService weDepartmentService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weDepartmentService.updateById(setWeDepartMent(message)); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java new file mode 100644 index 000000000..3cb69cf63 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java @@ -0,0 +1,42 @@ +package com.linkwechat.wecom.factory.impl.tag; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeTagGroupService; +import com.linkwechat.wecom.service.IWeTagService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 创建标签事件 + * @date 2021/1/21 1:18 + **/ +@Slf4j +@Component("createCustomerTag") +public class WeCallBackCreateCustomerTagImpl extends WeEventStrategy { + @Autowired + private IWeTagGroupService weTagGroupService; + @Autowired + private IWeTagService weTagService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + switch (message.getTagType()){ + case tagGroup: + weTagGroupService.createTagGroup(message.getId()); + break; + case tag: + weTagService.creatTag(message.getTagId()); + break; + default: + break; + } + } catch (Exception e) { + e.printStackTrace(); + log.error("createCustomerTag>>>>>>>>>param:{},ex:{}",message.getId(),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java new file mode 100644 index 000000000..c62eb4991 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java @@ -0,0 +1,41 @@ +package com.linkwechat.wecom.factory.impl.tag; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeTagGroupService; +import com.linkwechat.wecom.service.IWeTagService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 企业客户标签删除事件 + * @date 2021/1/21 1:18 + **/ +@Slf4j +@Component("deleteCustomerTag") +public class WeCallBackDeleteCustomerTagImpl extends WeEventStrategy { + @Autowired + private IWeTagGroupService weTagGroupService; + @Autowired + private IWeTagService weTagService; + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + switch (message.getTagType()){ + case tagGroup: + weTagGroupService.deleteTagGroup(message.getId()); + break; + case tag: + weTagService.deleteTag(message.getTagId()); + break; + default: + break; + } + } catch (Exception e) { + e.printStackTrace(); + log.error("deleteCustomerTag>>>>>>>>>param:{},ex:{}",message.getId(),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java new file mode 100644 index 000000000..b6cfa8921 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java @@ -0,0 +1,41 @@ +package com.linkwechat.wecom.factory.impl.tag; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeTagGroupService; +import com.linkwechat.wecom.service.IWeTagService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 企业客户标签变更事件 + * @date 2021/1/21 1:21 + **/ +@Slf4j +@Component("updateCustomerTag") +public class WeCallBackUpdateCustomerTagImpl extends WeEventStrategy { + @Autowired + private IWeTagGroupService weTagGroupService; + @Autowired + private IWeTagService weTagService; + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + switch (message.getTagType()){ + case tagGroup: + weTagGroupService.updateTagGroup(message.getId()); + break; + case tag: + weTagService.updateTag(message.getTagId()); + break; + default: + break; + } + } catch (Exception e) { + e.printStackTrace(); + log.error("updateCustomerTag>>>>>>>>>param:{},ex:{}",message.getId(),e); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateUserTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateUserTagImpl.java new file mode 100644 index 000000000..e9f446402 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateUserTagImpl.java @@ -0,0 +1,83 @@ +package com.linkwechat.wecom.factory.impl.tag; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.linkwechat.wecom.domain.WeFlowerCustomerRel; +import com.linkwechat.wecom.domain.WeFlowerCustomerTagRel; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeFlowerCustomerRelService; +import com.linkwechat.wecom.service.IWeFlowerCustomerTagRelService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author danmo + * @description 标签变更事件 + * @date 2021/1/20 23:10 + **/ +@Slf4j +@Component("update_tag") +public class WeCallBackUpdateUserTagImpl extends WeEventStrategy { + @Autowired + private IWeFlowerCustomerTagRelService weFlowerCustomerTagRelService; + @Autowired + private IWeFlowerCustomerRelService weFlowerCustomerRelService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + String tagId = message.getTagId(); + //标签中新增的成员userid列表,用逗号分隔 + List addUserItemsList = Arrays.stream(Optional.ofNullable(message.getAddUserItems()) + .orElse("").split(",")).collect(Collectors.toList()); + //标签中删除的成员userid列表,用逗号分隔 + List delUserItemsList = Arrays.stream(Optional.ofNullable(message.getDelUserItems()) + .orElse("").split(",")).collect(Collectors.toList()); + + //标签中新增的成员userid列表,建立关联 + List weFlowerCustomerTagRels = new ArrayList<>(); + LambdaQueryWrapper relLambdaQueryWrapper = new LambdaQueryWrapper<>(); + relLambdaQueryWrapper.in(WeFlowerCustomerRel::getUserId, addUserItemsList); + List flowerCustomerRelList = weFlowerCustomerRelService.list(relLambdaQueryWrapper); + List idList = Optional.ofNullable(flowerCustomerRelList).orElseGet(ArrayList::new) + .stream().map(WeFlowerCustomerRel::getId).collect(Collectors.toList()); + idList.forEach(id -> { + weFlowerCustomerTagRels.add(WeFlowerCustomerTagRel.builder().flowerCustomerRelId(id) + .tagId(tagId) + .build()); + }); + weFlowerCustomerTagRelService.batchInsetWeFlowerCustomerTagRel(weFlowerCustomerTagRels); + + //当前标签对应成员列表 + LambdaQueryWrapper tagRelLambdaQueryWrapper = new LambdaQueryWrapper<>(); + tagRelLambdaQueryWrapper.eq(WeFlowerCustomerTagRel::getTagId, tagId); + List tagRelList = weFlowerCustomerTagRelService.list(tagRelLambdaQueryWrapper); + + List flowerCustomerRelIdList = Optional.ofNullable(tagRelList).orElseGet(ArrayList::new) + .stream().map(WeFlowerCustomerTagRel::getFlowerCustomerRelId).collect(Collectors.toList()); + + if (!flowerCustomerRelIdList.isEmpty()) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(WeFlowerCustomerRel::getId, flowerCustomerRelIdList) + .in(WeFlowerCustomerRel::getUserId, delUserItemsList); + List relList = weFlowerCustomerRelService.list(queryWrapper); + List relIdList = Optional.ofNullable(relList).orElseGet(ArrayList::new) + .stream().map(WeFlowerCustomerRel::getId).collect(Collectors.toList()); + //标签中删除的成员userid列表 + LambdaQueryWrapper tagRelQueryWrapper = new LambdaQueryWrapper<>(); + tagRelQueryWrapper.in(WeFlowerCustomerTagRel::getFlowerCustomerRelId, relIdList); + weFlowerCustomerTagRelService.remove(tagRelQueryWrapper); + } + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackCreateUserImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackCreateUserImpl.java new file mode 100644 index 000000000..047d76d33 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackCreateUserImpl.java @@ -0,0 +1,30 @@ +package com.linkwechat.wecom.factory.impl.user; + +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 新建外部联系人事件 + * @date 2021/1/20 22:19 + **/ +@Slf4j +@Component("create_user") +public class WeCallBackCreateUserImpl extends WeEventStrategy { + @Autowired + private IWeUserService weUserService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weUserService.insertWeUserNoToWeCom(setWeUserData(message)); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackDeleteUserImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackDeleteUserImpl.java new file mode 100644 index 000000000..2af487f80 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackDeleteUserImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.user; + +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 删除成员事件 + * @date 2021/1/20 22:44 + **/ +@Slf4j +@Component("delete_user") +public class WeCallBackDeleteUserImpl extends WeEventStrategy { + @Autowired + private IWeUserService weUserService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weUserService.deleteUserNoToWeCom(message.getUserId()); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackUpdateUserImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackUpdateUserImpl.java new file mode 100644 index 000000000..41be034d6 --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/user/WeCallBackUpdateUserImpl.java @@ -0,0 +1,31 @@ +package com.linkwechat.wecom.factory.impl.user; + +import com.linkwechat.wecom.domain.WeUser; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeEventStrategy; +import com.linkwechat.wecom.service.IWeUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author danmo + * @description 成员更新事件 + * @date 2021/1/20 22:28 + **/ +@Slf4j +@Component("update_user") +public class WeCallBackUpdateUserImpl extends WeEventStrategy { + @Autowired + private IWeUserService weUserService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + try { + weUserService.updateWeUserNoToWeCom(setWeUserData(message)); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/interceptor/WeAccessTokenInterceptor.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/interceptor/WeAccessTokenInterceptor.java index 6b0e33de4..40ddd96a6 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/interceptor/WeAccessTokenInterceptor.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/interceptor/WeAccessTokenInterceptor.java @@ -62,6 +62,8 @@ public class WeAccessTokenInterceptor implements Interceptor{ token=iWeAccessTokenService.findProviderAccessToken(); }else if(Arrays.asList(weComeConfig.getNeedChatTokenUrl()).contains(uri)){ //需要会话存档token token=iWeAccessTokenService.findChatAccessToken(); + }else if(Arrays.asList(weComeConfig.getNeedAgentTokenUrl()).contains(uri)){ //需要应用token + token=iWeAccessTokenService.findAgentAccessToken(); }else{ token=iWeAccessTokenService.findCommonAccessToken(); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java index 886cb4f07..fa3d6f86c 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java @@ -51,7 +51,7 @@ public interface WeTagMapper extends BaseMapper * @param id 企业微信标签ID * @return 结果 */ - public int deleteWeTagById(Long id); + public int deleteWeTagById(String id); /** * 批量删除企业微信标签 @@ -59,7 +59,7 @@ public interface WeTagMapper extends BaseMapper * @param ids 需要删除的数据ID * @return 结果 */ - public int deleteWeTagByIds(Long[] ids); + public int deleteWeTagByIds(String[] ids); /** diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeUserMapper.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeUserMapper.java index 4a4e4c159..49e1b4abe 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeUserMapper.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeUserMapper.java @@ -20,10 +20,10 @@ public interface WeUserMapper extends BaseMapper /** * 查询通讯录相关客户 * - * @param id 通讯录相关客户ID + * @param userId 通讯录相关客户ID * @return 通讯录相关客户 */ - public WeUser selectWeUserById(Long id); + public WeUser selectWeUserById(String userId); /** * 查询通讯录相关客户列表 diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeAccessTokenService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeAccessTokenService.java index 6008c4ecf..cd558ff20 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeAccessTokenService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeAccessTokenService.java @@ -18,4 +18,6 @@ public interface IWeAccessTokenService { public String findChatAccessToken(); public void removeToken(); + + String findAgentAccessToken(); } \ No newline at end of file diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeConversationArchiveService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeConversationArchiveService.java index b3f98a53e..287f423bc 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeConversationArchiveService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeConversationArchiveService.java @@ -7,7 +7,7 @@ import com.linkwechat.common.core.domain.ConversationArchiveQuery; import java.util.List; /** - * @author sxw + * @author danmo * @description 会话存档业务接口 * @date 2020/12/19 13:59 **/ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeDepartmentService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeDepartmentService.java index 5dbb8c9f8..9e1436307 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeDepartmentService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeDepartmentService.java @@ -30,6 +30,8 @@ public interface IWeDepartmentService extends IService */ public void insertWeDepartment(WeDepartment weDepartment); + public int insertWeDepartmentNoToWeCom(WeDepartment weDepartment); + /** * 修改企业微信组织架构相关 * diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupService.java index 9f19b3ced..a043b1462 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupService.java @@ -20,4 +20,10 @@ public interface IWeGroupService extends IService { void synchWeGroup(); + + void createWeGroup(String chatId); + + void updateWeGroup(String chatId); + + void deleteWeGroup(String chatId); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagGroupService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagGroupService.java index eca1e96a2..b0e29ea21 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagGroupService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagGroupService.java @@ -63,4 +63,10 @@ public interface IWeTagGroupService extends IService * @param isSync */ public void batchSaveOrUpdateTagGroupAndTag(List tagGroup,Boolean isSync); + + void createTagGroup(String id); + + void deleteTagGroup(String id); + + void updateTagGroup(String id); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagService.java index 22cdd11ae..83155a6f9 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeTagService.java @@ -52,7 +52,7 @@ public interface IWeTagService extends IService * @param ids 需要删除的企业微信标签ID * @return 结果 */ - public int deleteWeTagByIds(Long[] ids); + public int deleteWeTagByIds(String[] ids); /** * 删除企业微信标签信息 @@ -60,7 +60,7 @@ public interface IWeTagService extends IService * @param id 企业微信标签ID * @return 结果 */ - public int deleteWeTagById(Long id); + public int deleteWeTagById(String id); /** @@ -70,4 +70,9 @@ public interface IWeTagService extends IService */ public int insertWeTagFromWeTagDto(WeTagDto weTagDto); + void creatTag(String tagId); + + void deleteTag(String tagId); + + void updateTag(String tagId); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeUserService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeUserService.java index a2990988c..5b46fc031 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeUserService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeUserService.java @@ -20,10 +20,10 @@ public interface IWeUserService extends IService /** * 查询通讯录相关客户 * - * @param id 通讯录相关客户ID + * @param userId 通讯录相关客户ID * @return 通讯录相关客户 */ - public WeUser selectWeUserById(Long id); + public WeUser selectWeUserById(String userId); /** * 查询通讯录相关客户列表 @@ -41,6 +41,13 @@ public interface IWeUserService extends IService */ public void insertWeUser(WeUser weUser); + /** + * 新增通讯录相关客户(不同步企微) + * @param weUser 通讯录相关客户 + * @return + */ + public int insertWeUserNoToWeCom(WeUser weUser); + /** * 修改通讯录相关客户 * @@ -49,6 +56,13 @@ public interface IWeUserService extends IService */ public void updateWeUser(WeUser weUser); + /** + * 修改通讯录相关客户(不同步企微) + * @param weUser 通讯录相关客户 + * @return + */ + public int updateWeUserNoToWeCom(WeUser weUser); + /** * 启用或禁用用户 @@ -92,6 +106,13 @@ public interface IWeUserService extends IService */ void deleteUser(String[] ids); + /** + * 删除成员 + * @param userId 成员id + * @return + */ + int deleteUserNoToWeCom(String userId); + /** * 获取历史分配记录的成员 diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeAccessTokenServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeAccessTokenServiceImpl.java index e03499b5f..00ca51a04 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeAccessTokenServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeAccessTokenServiceImpl.java @@ -106,6 +106,10 @@ public class WeAccessTokenServiceImpl implements IWeAccessTokenService { WeAccessTokenDtoDto weAccessTokenDtoDto = accessTokenClient.getToken(wxCorpAccount.getCorpId(),wxCorpAccount.getChatSecret()); token=weAccessTokenDtoDto.getAccess_token(); expires_in=weAccessTokenDtoDto.getExpires_in(); + }else if (WeConstans.WE_AGENT_ACCESS_TOKEN.equals(accessTokenKey)){ + WeAccessTokenDtoDto weAccessTokenDtoDto = accessTokenClient.getToken(wxCorpAccount.getCorpId(),wxCorpAccount.getAgentSecret()); + token=weAccessTokenDtoDto.getAccess_token(); + expires_in=weAccessTokenDtoDto.getExpires_in(); } if(StringUtils.isNotEmpty(token)){ @@ -129,5 +133,10 @@ public class WeAccessTokenServiceImpl implements IWeAccessTokenService { redisCache.deleteObject(WeConstans.WE_PROVIDER_ACCESS_TOKEN); } + @Override + public String findAgentAccessToken() { + return findAccessToken(WeConstans.WE_AGENT_ACCESS_TOKEN); + } + } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeConversationArchiveServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeConversationArchiveServiceImpl.java index 2812fab68..c329a887b 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeConversationArchiveServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeConversationArchiveServiceImpl.java @@ -24,7 +24,7 @@ import java.util.Date; import java.util.List; /** - * @author sxw + * @author danmo * @description 会话存档业务实现类 * @date 2020/12/19 14:00 **/ diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeCustomerServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeCustomerServiceImpl.java index 2f8b56d23..790d497c1 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeCustomerServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeCustomerServiceImpl.java @@ -476,7 +476,7 @@ public class WeCustomerServiceImpl extends ServiceImpl impl private WeCustomerClient weCustomerClient; + @Override public List selectWeGroupList(WeGroup weGroup) { return this.baseMapper.selectWeGroupList(weGroup); } @@ -208,7 +207,7 @@ public class WeGroupServiceImpl extends ServiceImpl impl List weGroupMemberList = iWeGroupMemberService.list(new LambdaQueryWrapper().in(WeGroupMember::getChatId, weGroups.stream().map(WeGroup::getChatId).collect(Collectors.toList()))); //存量去重 - if (CollectionUtil.isNotEmpty(weGroupMemberList)){ + if (CollectionUtil.isNotEmpty(weGroupMemberList)) { List groupMemberList = weGroupMembers.stream().filter(e -> { for (WeGroupMember weGroupMember : weGroupMemberList) { if (e.getUserId().equals(weGroupMember.getUserId())) { @@ -218,7 +217,7 @@ public class WeGroupServiceImpl extends ServiceImpl impl return true; }).collect(Collectors.toList()); iWeGroupMemberService.saveBatch(groupMemberList); - }else { + } else { iWeGroupMemberService.saveBatch(weGroupMembers); } } @@ -226,4 +225,111 @@ public class WeGroupServiceImpl extends ServiceImpl impl } + @Override + @Transactional(rollbackFor = Exception.class) + public void createWeGroup(String chatId) { + List weGroups = new ArrayList<>(); + List weGroupMembers = new ArrayList<>(); + CustomerGroupDetail customerGroupDetail = weCustomerGroupClient.groupChatDetail( + new CustomerGroupDetail().new Params(chatId) + ); + if (CollectionUtil.isNotEmpty(customerGroupDetail.getGroup_chat())) { + customerGroupDetail.getGroup_chat().stream().forEach(kk -> { + weGroups.add( + WeGroup.builder() + .chatId(kk.getChat_id()) + .groupName(kk.getName()) + .notice(kk.getNotice()) + .owner(kk.getOwner()) + .createTime(new Date(kk.getCreate_time() * 1000L)) + .build() + ); + List memberLists = kk.getMember_list(); + if (CollectionUtil.isNotEmpty(memberLists)) { + memberLists.stream().forEach(member -> { + weGroupMembers.add( + WeGroupMember.builder() + .chatId(kk.getChat_id()) + .userId(member.getUserid()) + .joinTime(new Date(member.getJoin_time() * 1000L)) + .joinScene(member.getJoin_scene()) + .joinType(member.getType()) + .unionId(member.getUnionid()) + .build() + ); + }); + } + }); + + this.saveOrUpdateBatch(weGroups); + iWeGroupMemberService.saveBatch(weGroupMembers); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateWeGroup(String chatId) { + List weGroups = new ArrayList<>(); + List weGroupMembers = new ArrayList<>(); + CustomerGroupDetail customerGroupDetail = weCustomerGroupClient.groupChatDetail( + new CustomerGroupDetail().new Params(chatId) + ); + if (CollectionUtil.isNotEmpty(customerGroupDetail.getGroup_chat())) { + customerGroupDetail.getGroup_chat().stream().forEach(kk -> { + weGroups.add( + WeGroup.builder() + .chatId(kk.getChat_id()) + .groupName(kk.getName()) + .notice(kk.getNotice()) + .owner(kk.getOwner()) + .createTime(new Date(kk.getCreate_time() * 1000L)) + .build() + ); + List memberLists = kk.getMember_list(); + if (CollectionUtil.isNotEmpty(memberLists)) { + memberLists.stream().forEach(member -> { + weGroupMembers.add( + WeGroupMember.builder() + .chatId(kk.getChat_id()) + .userId(member.getUserid()) + .joinTime(new Date(member.getJoin_time() * 1000L)) + .joinScene(member.getJoin_scene()) + .joinType(member.getType()) + .unionId(member.getUnionid()) + .build() + ); + }); + } + }); + + this.saveOrUpdateBatch(weGroups); + //获取表中成员信息 + List weGroupMemberList = iWeGroupMemberService.list(new LambdaQueryWrapper() + .eq(WeGroupMember::getChatId, chatId)); + if (weGroupMembers.size() > weGroupMemberList.size()) { + //成员信息取差集 + List list = weGroupMembers.stream().filter(m -> !weGroupMemberList.stream() + .map(d -> d.getUserId()).collect(Collectors.toList()).contains(m.getUserId())) + .collect(Collectors.toList()); + iWeGroupMemberService.saveBatch(list); + } else if (weGroupMembers.size() < weGroupMemberList.size()) { + //成员信息取差集 + List list = weGroupMemberList.stream().filter(m -> !weGroupMembers.stream() + .map(d -> d.getUserId()).collect(Collectors.toList()).contains(m.getUserId())) + .collect(Collectors.toList()); + iWeGroupMemberService.remove(new LambdaQueryWrapper() + .eq(WeGroupMember::getChatId, chatId) + .in(WeGroupMember::getUserId, list.stream().map(WeGroupMember::getUserId) + .collect(Collectors.toList()))); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteWeGroup(String chatId) { + this.baseMapper.delete(new LambdaQueryWrapper().eq(WeGroup::getChatId,chatId)); + iWeGroupMemberService.remove(new LambdaQueryWrapper().eq(WeGroupMember::getChatId,chatId)); + } + } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java index 365aa7f66..df84005dd 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java @@ -28,13 +28,12 @@ import java.util.stream.Collectors; /** * 标签组Service业务层处理 - * + * * @author ruoyi * @date 2020-09-07 */ @Service -public class WeTagGroupServiceImpl extends ServiceImpl implements IWeTagGroupService -{ +public class WeTagGroupServiceImpl extends ServiceImpl implements IWeTagGroupService { @Autowired private WeTagGroupMapper weTagGroupMapper; @@ -46,132 +45,122 @@ public class WeTagGroupServiceImpl extends ServiceImpl selectWeTagGroupList(WeTagGroup weTagGroup) - { + public List selectWeTagGroupList(WeTagGroup weTagGroup) { return weTagGroupMapper.selectWeTagGroupList(weTagGroup); } /** * 新增标签组 - * + * * @param weTagGroup 标签组 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) - public void insertWeTagGroup(WeTagGroup weTagGroup) - { + public void insertWeTagGroup(WeTagGroup weTagGroup) { List weTags = weTagGroup.getWeTags(); - if(CollectionUtil.isEmpty(weTags)){ + if (CollectionUtil.isEmpty(weTags)) { this.save(weTagGroup); - }else{ + } else { WeCropGropTagDtlDto weCropGropTagDtlDto = weCropTagClient.addCorpTag(WeCropGroupTagDto.transformAddTag(weTagGroup)); - if(weCropGropTagDtlDto.getErrcode().equals(WeConstans.WE_SUCCESS_CODE)){ - this.batchSaveOrUpdateTagGroupAndTag(ListUtil.toList(weCropGropTagDtlDto.getTag_group()),false); + if (weCropGropTagDtlDto.getErrcode().equals(WeConstans.WE_SUCCESS_CODE)) { + this.batchSaveOrUpdateTagGroupAndTag(ListUtil.toList(weCropGropTagDtlDto.getTag_group()), false); } } - } /** * 修改标签组 - * + * * @param weTagGroup 标签组 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) - public void updateWeTagGroup(WeTagGroup weTagGroup) - { - - List weTags = weTagGroup.getWeTags(); - //获取新增的集合 - if(CollectionUtil.isNotEmpty(weTags)) { - List filterWeTags = weTags.stream().filter(v -> StringUtils.isEmpty(v.getTagId())).collect(Collectors.toList()); - - //同步新增标签到微信端 - if(CollectionUtil.isNotEmpty(WeCropGroupTagDto.transformAddTag(weTagGroup).getTag())){ - WeCropGropTagDtlDto - weCropGropTagDtlDto = weCropTagClient.addCorpTag( WeCropGroupTagDto.transformAddTag(weTagGroup)); - if(weCropGropTagDtlDto.getErrcode().equals(WeConstans.WE_SUCCESS_CODE)){ - //微信端返回的标签主键,设置到weTags中 - Map weCropTagMap = weCropGropTagDtlDto.getTag_group().getTag().stream() - .collect(Collectors.toMap(weCropTagDto -> weCropTagDto.getName(), weCropTagDto -> weCropTagDto.getId())); - filterWeTags.stream().forEach(tag->{ - tag.setTagId(weCropTagMap.get(tag.getName())); - tag.setCreateTime(new Date()); - }); - } - } + public void updateWeTagGroup(WeTagGroup weTagGroup) { + List weTags = weTagGroup.getWeTags(); + //获取新增的集合 + if (CollectionUtil.isNotEmpty(weTags)) { + List filterWeTags = weTags.stream().filter(v -> StringUtils.isEmpty(v.getTagId())).collect(Collectors.toList()); + + //同步新增标签到微信端 + if (CollectionUtil.isNotEmpty(WeCropGroupTagDto.transformAddTag(weTagGroup).getTag())) { + WeCropGropTagDtlDto + weCropGropTagDtlDto = weCropTagClient.addCorpTag(WeCropGroupTagDto.transformAddTag(weTagGroup)); + if (weCropGropTagDtlDto.getErrcode().equals(WeConstans.WE_SUCCESS_CODE)) { + //微信端返回的标签主键,设置到weTags中 + Map weCropTagMap = weCropGropTagDtlDto.getTag_group().getTag().stream() + .collect(Collectors.toMap(weCropTagDto -> weCropTagDto.getName(), weCropTagDto -> weCropTagDto.getId())); + filterWeTags.stream().forEach(tag -> { + tag.setTagId(weCropTagMap.get(tag.getName())); + tag.setCreateTime(new Date()); + }); + } + } - //获取需要删除的数据 - List removeWeTags = iWeTagService.list(new LambdaQueryWrapper().notIn(WeTag::getTagId, - weTags.stream().map(WeTag::getTagId).collect(Collectors.toList())).eq(WeTag::getGroupId,weTagGroup.getGroupId()) - .eq(WeTag::getStatus,Constants.NORMAL_CODE)); + //获取需要删除的数据 + List removeWeTags = iWeTagService.list(new LambdaQueryWrapper().notIn(WeTag::getTagId, + weTags.stream().map(WeTag::getTagId).collect(Collectors.toList())).eq(WeTag::getGroupId, weTagGroup.getGroupId()) + .eq(WeTag::getStatus, Constants.NORMAL_CODE)); - if (CollectionUtil.isNotEmpty(removeWeTags)) { - //同步删除微信端的标签 - weCropTagClient.delCorpTag( - WeCropDelDto.builder() - .group_id(ArrayUtil.toArray(ListUtil.toList(weTagGroup.getGroupId()), String.class)) - .tag_id(ArrayUtil.toArray(removeWeTags.stream().map(WeTag::getTagId).collect(Collectors.toList()), String.class)) - .build()); + if (CollectionUtil.isNotEmpty(removeWeTags)) { + //同步删除微信端的标签 + weCropTagClient.delCorpTag( + WeCropDelDto.builder() + .group_id(ArrayUtil.toArray(ListUtil.toList(weTagGroup.getGroupId()), String.class)) + .tag_id(ArrayUtil.toArray(removeWeTags.stream().map(WeTag::getTagId).collect(Collectors.toList()), String.class)) + .build()); - //移除本地 - removeWeTags.stream().forEach(v -> v.setStatus(Constants.DELETE_CODE)); - iWeTagService.updateBatchById(removeWeTags); - } + //移除本地 + removeWeTags.stream().forEach(v -> v.setStatus(Constants.DELETE_CODE)); + iWeTagService.updateBatchById(removeWeTags); + } - //保存或更新wetag - filterWeTags.stream().forEach(v->v.setGroupId(weTagGroup.getGroupId())); - iWeTagService.saveOrUpdateBatch(filterWeTags); + //保存或更新wetag + filterWeTags.stream().forEach(v -> v.setGroupId(weTagGroup.getGroupId())); + iWeTagService.saveOrUpdateBatch(filterWeTags); - } + } } /** * 批量删除标签组 - * + * * @param ids 需要删除的标签组ID * @return 结果 */ @Override @Transactional - public int deleteWeTagGroupByIds(String[] ids) - { - + public int deleteWeTagGroupByIds(String[] ids) { int returnCode = weTagGroupMapper.deleteWeTagGroupByIds(ids); - if(returnCode> Constants.SERVICE_RETURN_SUCCESS_CODE){ + if (returnCode > Constants.SERVICE_RETURN_SUCCESS_CODE) { List weTags = iWeTagService.list(new LambdaQueryWrapper().in(WeTag::getGroupId, ids)); - if(CollectionUtil.isNotEmpty(weTags)){ + if (CollectionUtil.isNotEmpty(weTags)) { weCropTagClient.delCorpTag( WeCropDelDto.builder() .group_id(ids) @@ -186,8 +175,6 @@ public class WeTagGroupServiceImpl extends ServiceImpl tagGroup,Boolean isSync) { + public void batchSaveOrUpdateTagGroupAndTag(List tagGroup, Boolean isSync) { - List weTagGroups=new ArrayList<>(); + List weTagGroups = new ArrayList<>(); - if(CollectionUtil.isNotEmpty(tagGroup)){ - tagGroup.stream().forEach(k->{ - WeTagGroup weTagGroup=new WeTagGroup(); + if (CollectionUtil.isNotEmpty(tagGroup)) { + tagGroup.stream().forEach(k -> { + WeTagGroup weTagGroup = new WeTagGroup(); weTagGroup.setCreateBy(SecurityUtils.getUsername()); weTagGroup.setGourpName(k.getGroup_name()); weTagGroup.setGroupId(k.getGroup_id()); List tag = k.getTag(); - if(CollectionUtil.isNotEmpty(tag)){ - List weTags=new ArrayList<>(); - tag.stream().forEach(v->{ - WeTag weTag=new WeTag(); + if (CollectionUtil.isNotEmpty(tag)) { + List weTags = new ArrayList<>(); + tag.stream().forEach(v -> { + WeTag weTag = new WeTag(); weTag.setTagId(v.getId()); weTag.setGroupId(weTagGroup.getGroupId()); weTag.setName(v.getName()); @@ -242,14 +230,14 @@ public class WeTagGroupServiceImpl extends ServiceImpl noExist = this.list(new LambdaQueryWrapper().notIn(WeTagGroup::getGroupId, weTagGroups.stream().map(WeTagGroup::getGroupId).collect(Collectors.toList()))); //企业微信端删除得标签 - if(CollectionUtil.isNotEmpty(noExist)){ - noExist.stream().forEach(k->k.setStatus(Constants.DELETE_CODE)); + if (CollectionUtil.isNotEmpty(noExist)) { + noExist.stream().forEach(k -> k.setStatus(Constants.DELETE_CODE)); this.updateBatchById(noExist); } } @@ -260,14 +248,14 @@ public class WeTagGroupServiceImpl extends ServiceImpl weTags = weTagGroups.stream().map(WeTagGroup::getWeTags).collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll); - if(CollectionUtil.isNotEmpty(weTags)){ + if (CollectionUtil.isNotEmpty(weTags)) { - if(isSync){ + if (isSync) { List noExistWeTags = iWeTagService.list(new LambdaQueryWrapper().notIn(WeTag::getTagId, weTags.stream().map(WeTag::getTagId).collect(Collectors.toList()))); - if(CollectionUtil.isNotEmpty(noExistWeTags)){ - noExistWeTags.stream().forEach(k->k.setStatus(Constants.DELETE_CODE)); + if (CollectionUtil.isNotEmpty(noExistWeTags)) { + noExistWeTags.stream().forEach(k -> k.setStatus(Constants.DELETE_CODE)); iWeTagService.updateBatchById(noExistWeTags); } @@ -279,34 +267,108 @@ public class WeTagGroupServiceImpl extends ServiceImpl weTagGroupList = this.list(new LambdaQueryWrapper().eq(WeTagGroup::getStatus, Constants.NORMAL_CODE)); - if(CollectionUtil.isNotEmpty(weTagGroupList)){ - weTagGroupList.stream().forEach(k->k.setStatus(Constants.DELETE_CODE)); + if (CollectionUtil.isNotEmpty(weTagGroupList)) { + weTagGroupList.stream().forEach(k -> k.setStatus(Constants.DELETE_CODE)); this.updateBatchById(weTagGroupList); } List weTags = iWeTagService.list(new LambdaQueryWrapper().eq(WeTag::getStatus, Constants.NORMAL_CODE)); - if(CollectionUtil.isNotEmpty(weTags)){ - weTags.stream().forEach(k->k.setStatus(Constants.DELETE_CODE)); + if (CollectionUtil.isNotEmpty(weTags)) { + weTags.stream().forEach(k -> k.setStatus(Constants.DELETE_CODE)); iWeTagService.updateBatchById(weTags); } } - } + } - - - - + @Override + public void createTagGroup(String id) { + List list = new ArrayList<>(); + list.add(id); + WeFindCropTagParam build = WeFindCropTagParam.builder().group_id(list).build(); + WeCropGroupTagListDto weCropGroupTagListDto = weCropTagClient.getCorpTagListByTagIds(build); + List tag_group = weCropGroupTagListDto.getTag_group(); + if (CollectionUtil.isNotEmpty(tag_group)) { + List tagGroupsList = new ArrayList<>(); + tag_group.stream().forEach(k -> { + WeTagGroup tagGroupInfo = this.baseMapper.selectOne(new LambdaQueryWrapper() + .eq(WeTagGroup::getGroupId, k.getGroup_id())); + if (tagGroupInfo != null) { + WeTagGroup weTagGroup = new WeTagGroup(); + weTagGroup.setCreateBy(SecurityUtils.getUsername()); + weTagGroup.setGourpName(k.getGroup_name()); + weTagGroup.setGroupId(k.getGroup_id()); + weTagGroup.setStatus(k.getDeleted()==true?"0":"2"); + tagGroupsList.add(weTagGroup); + } + List tag = k.getTag(); + if (CollectionUtil.isNotEmpty(tag)) { + List weTags = new ArrayList<>(); + tag.stream().forEach(v -> { + WeTag tagInfo = iWeTagService.getOne(new LambdaQueryWrapper() + .eq(WeTag::getGroupId, k.getGroup_id()) + .eq(WeTag::getTagId, v.getId())); + if (tagInfo != null) { + WeTag weTag = new WeTag(); + weTag.setTagId(v.getId()); + weTag.setGroupId(k.getGroup_id()); + weTag.setName(v.getName()); + weTag.setStatus(v.getDeleted()==true?"0":"2"); + weTags.add(weTag); + } + }); + iWeTagService.saveBatch(weTags); + } + }); + this.saveBatch(tagGroupsList); + } } + @Override + public void deleteTagGroup(String id) { + this.deleteWeTagGroupByIds(id.split(",")); + } + @Override + public void updateTagGroup(String id) { + List list = new ArrayList<>(); + list.add(id); + WeFindCropTagParam build = WeFindCropTagParam.builder().group_id(list).build(); + WeCropGroupTagListDto weCropGroupTagListDto = weCropTagClient.getCorpTagListByTagIds(build); + List tag_group = weCropGroupTagListDto.getTag_group(); + if (CollectionUtil.isNotEmpty(tag_group)) { + List tagGroupsList = new ArrayList<>(); + WeTagGroup weTagGroup = new WeTagGroup(); + tag_group.stream().forEach(k -> { + weTagGroup.setCreateBy(SecurityUtils.getUsername()); + weTagGroup.setGourpName(k.getGroup_name()); + weTagGroup.setGroupId(k.getGroup_id()); + weTagGroup.setStatus(k.getDeleted()==true?"0":"2"); + tagGroupsList.add(weTagGroup); + List tag = k.getTag(); + if (CollectionUtil.isNotEmpty(tag)) { + List weTags = new ArrayList<>(); + WeTag weTag = new WeTag(); + tag.stream().forEach(v -> { + weTag.setTagId(v.getId()); + weTag.setGroupId(k.getGroup_id()); + weTag.setName(v.getName()); + weTag.setStatus(v.getDeleted()==true?"0":"2"); + weTags.add(weTag); + }); + iWeTagService.saveOrUpdateBatch(weTags); + } + }); + this.saveOrUpdateBatch(tagGroupsList); + } + } } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java index 713e8659b..ba69b3dd1 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java @@ -1,10 +1,22 @@ package com.linkwechat.wecom.service.impl; +import java.util.ArrayList; import java.util.List; +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.linkwechat.common.utils.DateUtils; +import com.linkwechat.common.utils.SecurityUtils; +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.wecom.client.WeCropTagClient; +import com.linkwechat.wecom.domain.WeTagGroup; import com.linkwechat.wecom.domain.dto.WeTagDto; +import com.linkwechat.wecom.domain.dto.tag.WeCropGroupTagDto; +import com.linkwechat.wecom.domain.dto.tag.WeCropGroupTagListDto; +import com.linkwechat.wecom.domain.dto.tag.WeCropTagDto; +import com.linkwechat.wecom.domain.dto.tag.WeFindCropTagParam; +import com.linkwechat.wecom.service.IWeTagGroupService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.linkwechat.wecom.mapper.WeTagMapper; @@ -22,6 +34,10 @@ public class WeTagServiceImpl extends ServiceImpl implements { @Autowired private WeTagMapper weTagMapper; + @Autowired + private WeCropTagClient weCropTagClient; + @Autowired + private IWeTagGroupService weTagGroupService; /** * 查询企业微信标签 @@ -79,7 +95,7 @@ public class WeTagServiceImpl extends ServiceImpl implements * @return 结果 */ @Override - public int deleteWeTagByIds(Long[] ids) + public int deleteWeTagByIds(String[] ids) { return weTagMapper.deleteWeTagByIds(ids); } @@ -91,7 +107,7 @@ public class WeTagServiceImpl extends ServiceImpl implements * @return 结果 */ @Override - public int deleteWeTagById(Long id) + public int deleteWeTagById(String id) { return weTagMapper.deleteWeTagById(id); } @@ -115,4 +131,80 @@ public class WeTagServiceImpl extends ServiceImpl implements return 0; } + + @Override + public void creatTag(String tagId) { + if (StringUtils.isNotEmpty(tagId)){ + WeCropGroupTagListDto weCropGroupTagListDto = weCropTagClient.getCorpTagListByTagIds(WeFindCropTagParam.builder().tag_id(tagId.split(",")).build()); + List tag_group = weCropGroupTagListDto.getTag_group(); + if (CollectionUtil.isNotEmpty(tag_group)) { + List tagGroupsList = new ArrayList<>(); + tag_group.stream().forEach(k -> { + WeTagGroup tagGroupInfo = this.weTagGroupService.getOne(new LambdaQueryWrapper() + .eq(WeTagGroup::getGroupId, k.getGroup_id())); + if (tagGroupInfo != null) { + WeTagGroup weTagGroup = new WeTagGroup(); + weTagGroup.setCreateBy(SecurityUtils.getUsername()); + weTagGroup.setGourpName(k.getGroup_name()); + weTagGroup.setGroupId(k.getGroup_id()); + tagGroupsList.add(weTagGroup); + } + List tag = k.getTag(); + if (CollectionUtil.isNotEmpty(tag)) { + List weTags = new ArrayList<>(); + tag.stream().forEach(v -> { + WeTag tagInfo = this.getOne(new LambdaQueryWrapper() + .eq(WeTag::getGroupId, k.getGroup_id()) + .eq(WeTag::getTagId, v.getId())); + if (tagInfo != null) { + WeTag weTag = new WeTag(); + weTag.setTagId(v.getId()); + weTag.setGroupId(k.getGroup_id()); + weTag.setName(v.getName()); + weTags.add(weTag); + } + }); + this.saveBatch(weTags); + } + }); + weTagGroupService.saveBatch(tagGroupsList); + } + } + } + + @Override + public void deleteTag(String tagId) { + this.deleteWeTagById(tagId); + } + + @Override + public void updateTag(String tagId) { + if (StringUtils.isNotEmpty(tagId)){ + WeCropGroupTagListDto weCropGroupTagListDto = weCropTagClient.getCorpTagListByTagIds(WeFindCropTagParam.builder().tag_id(tagId.split(",")).build()); + List tag_group = weCropGroupTagListDto.getTag_group(); + if (CollectionUtil.isNotEmpty(tag_group)) { + List tagGroupsList = new ArrayList<>(); + tag_group.stream().forEach(k -> { + WeTagGroup weTagGroup = new WeTagGroup(); + weTagGroup.setCreateBy(SecurityUtils.getUsername()); + weTagGroup.setGourpName(k.getGroup_name()); + weTagGroup.setGroupId(k.getGroup_id()); + tagGroupsList.add(weTagGroup); + List tag = k.getTag(); + if (CollectionUtil.isNotEmpty(tag)) { + List weTags = new ArrayList<>(); + tag.stream().forEach(v -> { + WeTag weTag = new WeTag(); + weTag.setTagId(v.getId()); + weTag.setGroupId(k.getGroup_id()); + weTag.setName(v.getName()); + weTags.add(weTag); + }); + this.saveOrUpdateBatch(weTags); + } + }); + weTagGroupService.saveOrUpdateBatch(tagGroupsList); + } + } + } } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeUserServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeUserServiceImpl.java index 905dd9d3a..42455e33d 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeUserServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeUserServiceImpl.java @@ -62,13 +62,13 @@ public class WeUserServiceImpl extends ServiceImpl implemen /** * 查询通讯录相关客户 * - * @param id 通讯录相关客户ID + * @param userId 通讯录相关客户ID * @return 通讯录相关客户 */ @Override - public WeUser selectWeUserById(Long id) + public WeUser selectWeUserById(String userId) { - return weUserMapper.selectWeUserById(id); + return weUserMapper.selectWeUserById(userId); } /** @@ -103,6 +103,18 @@ public class WeUserServiceImpl extends ServiceImpl implemen } + @Override + @Transactional + public int insertWeUserNoToWeCom(WeUser weUser) + { + WeUser weUserInfo = weUserMapper.selectWeUserById(weUser.getUserId()); + if (weUserInfo != null){ + return weUserMapper.updateWeUser(weUser); + } + return weUserMapper.insertWeUser(weUser); + + } + /** * 修改通讯录相关客户 * @@ -121,6 +133,13 @@ public class WeUserServiceImpl extends ServiceImpl implemen } } + @Override + @Transactional + public int updateWeUserNoToWeCom(WeUser weUser) + { + return weUserMapper.updateWeUser(weUser); + } + @@ -231,6 +250,17 @@ public class WeUserServiceImpl extends ServiceImpl implemen } + @Override + @Transactional + public int deleteUserNoToWeCom(String userId) { + WeUser weUser = WeUser.builder() + .userId(userId) + .isActivate(WeConstans.WE_USER_IS_LEAVE) + .dimissionTime(new Date()) + .build(); + return weUserMapper.updateById(weUser); + } + /** * 获取历史分配记录的成员 diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/strategy/SendMessageToUserStrategy.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/strategy/SendMessageToUserStrategy.java index 6b3275bf4..f55a0254b 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/strategy/SendMessageToUserStrategy.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/strategy/SendMessageToUserStrategy.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Optional; +import java.util.stream.Collector; +import java.util.stream.Collectors; /** * 发送应用消息 @@ -42,9 +44,18 @@ public class SendMessageToUserStrategy implements Strategy { of.ifPresent(messageType -> map.put(messageType.getMessageType(), jsonObject)); //发送消息 WeMessagePushDto weMessagePushDto = new WeMessagePushDto(); - weMessagePushDto.setTouser(StringUtils.isNotBlank(weMessagePush.getToUser()) ? Arrays.asList(weMessagePush.getToUser().split(",")) : new ArrayList<>()); - weMessagePushDto.setToparty(StringUtils.isNotBlank(weMessagePush.getToUser()) ? Arrays.asList(weMessagePush.getToParty().split(",")) : new ArrayList<>()); - weMessagePushDto.setTotag(StringUtils.isNotBlank(weMessagePush.getToUser()) ? Arrays.asList(weMessagePush.getToTag().split(",")) : new ArrayList<>()); + Optional.ofNullable(weMessagePush.getToUser()).ifPresent(userId ->{ + String tousers = Arrays.stream(userId.split(",")).collect(Collectors.joining("|")); + weMessagePushDto.setTouser(tousers); + }); + Optional.ofNullable(weMessagePush.getToParty()).ifPresent(partyId ->{ + String toPartys = Arrays.stream(partyId.split(",")).collect(Collectors.joining("|")); + weMessagePushDto.setToparty(toPartys); + }); + Optional.ofNullable(weMessagePush.getToTag()).ifPresent(tagId ->{ + String tagIds = Arrays.stream(tagId.split(",")).collect(Collectors.joining("|")); + weMessagePushDto.setTotag(tagIds); + }); weMessagePushDto.setMsgtype(weMessagePush.getMessageType()); //这个先写在配置文件中 diff --git a/linkwe-wecom/src/main/resources/mapper/wecom/WeCorpAccountMapper.xml b/linkwe-wecom/src/main/resources/mapper/wecom/WeCorpAccountMapper.xml index d1d3b7700..a5861ccea 100644 --- a/linkwe-wecom/src/main/resources/mapper/wecom/WeCorpAccountMapper.xml +++ b/linkwe-wecom/src/main/resources/mapper/wecom/WeCorpAccountMapper.xml @@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -24,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id,chat_secret,provider_secret,company_name, corp_id, corp_secret,agent_id ,status, del_flag, create_by, create_time, update_by, update_time,contact_secret, customer_churn_notice_switch,wx_qr_login_redirect_uri from we_corp_account + select id,chat_secret,provider_secret,company_name, corp_id, corp_secret,agent_id, agent_secret, status, del_flag, create_by, create_time, update_by, update_time,contact_secret, customer_churn_notice_switch,wx_qr_login_redirect_uri from we_corp_account - - where id = #{id} + where user_id = #{userId} insert into we_user - id, + user_id, head_image_url, user_name, alias, - user_id, gender, mobile, email, @@ -85,11 +84,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" is_activate, - #{id}, + #{userId}, #{avatarMediaid}, #{name}, #{alias}, - #{userId}, #{gender}, #{mobile}, #{email}, @@ -133,7 +131,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" remark = #{remark}, is_activate = #{isActivate}, - where id = #{id} + + user_id = #{userId} + @@ -191,7 +191,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - insert into we_user (id, head_image_url, user_name, alias, + insert into we_user (head_image_url, user_name, alias, user_id, gender, mobile, email, wx_account, department, position, is_leader_in_dept, join_time, enable, id_card, @@ -199,7 +199,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" birthday, remark, is_activate) values - ( #{weUser.id},#{weUser.avatarMediaid},#{weUser.name},#{weUser.alias}, + ( #{weUser.avatarMediaid},#{weUser.name},#{weUser.alias}, #{weUser.userId},#{weUser.gender},#{weUser.mobile},#{weUser.email},#{weUser.wxAccount}, #{weUser.department},#{weUser.position},#{weUser.isLeaderInDept}, #{weUser.joinTime},#{weUser.enable},#{weUser.idCard}, -- Gitee From 6a1a3616c87405f79880f94f4627bf75af246f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=96=9C=E6=97=BA?= Date: Sat, 23 Jan 2021 02:02:42 +0800 Subject: [PATCH 051/177] =?UTF-8?q?=E6=A0=87=E7=AD=BE=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wecom/domain/vo/WxCpXmlMessageVO.java | 2 +- .../wecom/factory/WeEventStrategy.java | 2 +- .../tag/WeCallBackCreateCustomerTagImpl.java | 2 +- .../tag/WeCallBackDeleteCustomerTagImpl.java | 2 +- .../tag/WeCallBackUpdateCustomerTagImpl.java | 2 +- .../linkwechat/wecom/mapper/WeTagMapper.java | 5 ++- .../service/impl/WeTagGroupServiceImpl.java | 44 +++---------------- .../wecom/service/impl/WeTagServiceImpl.java | 20 +-------- .../resources/mapper/wecom/WeTagMapper.xml | 6 +-- 9 files changed, 18 insertions(+), 67 deletions(-) diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java index 1cf52f346..337683294 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/vo/WxCpXmlMessageVO.java @@ -39,7 +39,7 @@ public class WxCpXmlMessageVO extends WxCpXmlMessage { @XStreamAlias("Id") @XStreamConverter(value = XStreamCDataConverter.class) - private String id; + private String tagId; @Data public static class BatchJob implements Serializable { diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java index 3040e5c3f..f05894845 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/WeEventStrategy.java @@ -50,7 +50,7 @@ public abstract class WeEventStrategy { public WeDepartment setWeDepartMent(WxCpXmlMessageVO message){ WeDepartment weDepartment = new WeDepartment(); if (message.getId() != null) { - weDepartment.setId(Long.valueOf(message.getId())); + weDepartment.setId(message.getId()); } if (message.getName() != null) { weDepartment.setName(message.getName()); diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java index 3cb69cf63..1d9aee4f9 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackCreateCustomerTagImpl.java @@ -26,7 +26,7 @@ public class WeCallBackCreateCustomerTagImpl extends WeEventStrategy { try { switch (message.getTagType()){ case tagGroup: - weTagGroupService.createTagGroup(message.getId()); + weTagGroupService.createTagGroup(message.getTagId()); break; case tag: weTagService.creatTag(message.getTagId()); diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java index c62eb4991..b99735976 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackDeleteCustomerTagImpl.java @@ -25,7 +25,7 @@ public class WeCallBackDeleteCustomerTagImpl extends WeEventStrategy { try { switch (message.getTagType()){ case tagGroup: - weTagGroupService.deleteTagGroup(message.getId()); + weTagGroupService.deleteTagGroup(message.getTagId()); break; case tag: weTagService.deleteTag(message.getTagId()); diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java index b6cfa8921..63381fe92 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/tag/WeCallBackUpdateCustomerTagImpl.java @@ -25,7 +25,7 @@ public class WeCallBackUpdateCustomerTagImpl extends WeEventStrategy { try { switch (message.getTagType()){ case tagGroup: - weTagGroupService.updateTagGroup(message.getId()); + weTagGroupService.updateTagGroup(message.getTagId()); break; case tag: weTagService.updateTag(message.getTagId()); diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java index fa3d6f86c..1e7a4d9f0 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeTagMapper.java @@ -4,6 +4,7 @@ import java.util.List; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.linkwechat.wecom.domain.WeTag; +import org.apache.ibatis.annotations.Param; /** * 企业微信标签Mapper接口 @@ -51,7 +52,7 @@ public interface WeTagMapper extends BaseMapper * @param id 企业微信标签ID * @return 结果 */ - public int deleteWeTagById(String id); + public int deleteWeTagById(@Param("id") String id); /** * 批量删除企业微信标签 @@ -59,7 +60,7 @@ public interface WeTagMapper extends BaseMapper * @param ids 需要删除的数据ID * @return 结果 */ - public int deleteWeTagByIds(String[] ids); + public int deleteWeTagByIds(@Param("ids") String[] ids); /** diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java index df84005dd..cefe7cec5 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagGroupServiceImpl.java @@ -299,33 +299,15 @@ public class WeTagGroupServiceImpl extends ServiceImpl tagGroupsList = new ArrayList<>(); tag_group.stream().forEach(k -> { WeTagGroup tagGroupInfo = this.baseMapper.selectOne(new LambdaQueryWrapper() - .eq(WeTagGroup::getGroupId, k.getGroup_id())); - if (tagGroupInfo != null) { + .eq(WeTagGroup::getGroupId, k.getGroup_id()) + .eq(WeTagGroup::getStatus,0)); + if (tagGroupInfo == null) { WeTagGroup weTagGroup = new WeTagGroup(); - weTagGroup.setCreateBy(SecurityUtils.getUsername()); + weTagGroup.setCreateBy("sys"); weTagGroup.setGourpName(k.getGroup_name()); weTagGroup.setGroupId(k.getGroup_id()); - weTagGroup.setStatus(k.getDeleted()==true?"0":"2"); tagGroupsList.add(weTagGroup); } - List tag = k.getTag(); - if (CollectionUtil.isNotEmpty(tag)) { - List weTags = new ArrayList<>(); - tag.stream().forEach(v -> { - WeTag tagInfo = iWeTagService.getOne(new LambdaQueryWrapper() - .eq(WeTag::getGroupId, k.getGroup_id()) - .eq(WeTag::getTagId, v.getId())); - if (tagInfo != null) { - WeTag weTag = new WeTag(); - weTag.setTagId(v.getId()); - weTag.setGroupId(k.getGroup_id()); - weTag.setName(v.getName()); - weTag.setStatus(v.getDeleted()==true?"0":"2"); - weTags.add(weTag); - } - }); - iWeTagService.saveBatch(weTags); - } }); this.saveBatch(tagGroupsList); } @@ -333,7 +315,7 @@ public class WeTagGroupServiceImpl extends ServiceImpl tagGroupsList = new ArrayList<>(); WeTagGroup weTagGroup = new WeTagGroup(); tag_group.stream().forEach(k -> { - weTagGroup.setCreateBy(SecurityUtils.getUsername()); + weTagGroup.setCreateBy("sys"); weTagGroup.setGourpName(k.getGroup_name()); weTagGroup.setGroupId(k.getGroup_id()); - weTagGroup.setStatus(k.getDeleted()==true?"0":"2"); tagGroupsList.add(weTagGroup); - List tag = k.getTag(); - if (CollectionUtil.isNotEmpty(tag)) { - List weTags = new ArrayList<>(); - WeTag weTag = new WeTag(); - tag.stream().forEach(v -> { - weTag.setTagId(v.getId()); - weTag.setGroupId(k.getGroup_id()); - weTag.setName(v.getName()); - weTag.setStatus(v.getDeleted()==true?"0":"2"); - weTags.add(weTag); - }); - iWeTagService.saveOrUpdateBatch(weTags); - } }); this.saveOrUpdateBatch(tagGroupsList); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java index ba69b3dd1..fd5e7f530 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeTagServiceImpl.java @@ -138,17 +138,7 @@ public class WeTagServiceImpl extends ServiceImpl implements WeCropGroupTagListDto weCropGroupTagListDto = weCropTagClient.getCorpTagListByTagIds(WeFindCropTagParam.builder().tag_id(tagId.split(",")).build()); List tag_group = weCropGroupTagListDto.getTag_group(); if (CollectionUtil.isNotEmpty(tag_group)) { - List tagGroupsList = new ArrayList<>(); tag_group.stream().forEach(k -> { - WeTagGroup tagGroupInfo = this.weTagGroupService.getOne(new LambdaQueryWrapper() - .eq(WeTagGroup::getGroupId, k.getGroup_id())); - if (tagGroupInfo != null) { - WeTagGroup weTagGroup = new WeTagGroup(); - weTagGroup.setCreateBy(SecurityUtils.getUsername()); - weTagGroup.setGourpName(k.getGroup_name()); - weTagGroup.setGroupId(k.getGroup_id()); - tagGroupsList.add(weTagGroup); - } List tag = k.getTag(); if (CollectionUtil.isNotEmpty(tag)) { List weTags = new ArrayList<>(); @@ -156,7 +146,7 @@ public class WeTagServiceImpl extends ServiceImpl implements WeTag tagInfo = this.getOne(new LambdaQueryWrapper() .eq(WeTag::getGroupId, k.getGroup_id()) .eq(WeTag::getTagId, v.getId())); - if (tagInfo != null) { + if (tagInfo == null) { WeTag weTag = new WeTag(); weTag.setTagId(v.getId()); weTag.setGroupId(k.getGroup_id()); @@ -167,7 +157,6 @@ public class WeTagServiceImpl extends ServiceImpl implements this.saveBatch(weTags); } }); - weTagGroupService.saveBatch(tagGroupsList); } } } @@ -183,13 +172,7 @@ public class WeTagServiceImpl extends ServiceImpl implements WeCropGroupTagListDto weCropGroupTagListDto = weCropTagClient.getCorpTagListByTagIds(WeFindCropTagParam.builder().tag_id(tagId.split(",")).build()); List tag_group = weCropGroupTagListDto.getTag_group(); if (CollectionUtil.isNotEmpty(tag_group)) { - List tagGroupsList = new ArrayList<>(); tag_group.stream().forEach(k -> { - WeTagGroup weTagGroup = new WeTagGroup(); - weTagGroup.setCreateBy(SecurityUtils.getUsername()); - weTagGroup.setGourpName(k.getGroup_name()); - weTagGroup.setGroupId(k.getGroup_id()); - tagGroupsList.add(weTagGroup); List tag = k.getTag(); if (CollectionUtil.isNotEmpty(tag)) { List weTags = new ArrayList<>(); @@ -203,7 +186,6 @@ public class WeTagServiceImpl extends ServiceImpl implements this.saveOrUpdateBatch(weTags); } }); - weTagGroupService.saveOrUpdateBatch(tagGroupsList); } } } diff --git a/linkwe-wecom/src/main/resources/mapper/wecom/WeTagMapper.xml b/linkwe-wecom/src/main/resources/mapper/wecom/WeTagMapper.xml index d898b2f03..5679371a4 100644 --- a/linkwe-wecom/src/main/resources/mapper/wecom/WeTagMapper.xml +++ b/linkwe-wecom/src/main/resources/mapper/wecom/WeTagMapper.xml @@ -54,12 +54,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where id = #{id} - - uddate we_tag set status=2 where tag_id = #{id} + + update we_tag set status=2 where tag_id = #{id} - uddate we_tag set status=2 where tag_id in + update we_tag set status=2 where tag_id in #{id} -- Gitee From 5a82af688f512b479460f8b2fd0659b812a6097a Mon Sep 17 00:00:00 2001 From: xinla <1570728529@qq.com> Date: Sun, 24 Jan 2021 22:49:44 +0800 Subject: [PATCH 052/177] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E6=A0=8F=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linkwe-mobile/src/App.vue | 4 +- linkwe-mobile/src/api/chat.js | 16 +++--- linkwe-mobile/src/api/common.js | 13 +++-- linkwe-mobile/src/utils/request.js | 7 ++- linkwe-mobile/src/views/chat/List.vue | 49 +++++++++++++------ linkwe-mobile/src/views/chat/index.vue | 67 ++++++++++++++++++++++---- 6 files changed, 111 insertions(+), 45 deletions(-) diff --git a/linkwe-mobile/src/App.vue b/linkwe-mobile/src/App.vue index 56e423dd8..3f4a444b6 100644 --- a/linkwe-mobile/src/App.vue +++ b/linkwe-mobile/src/App.vue @@ -4,8 +4,8 @@ export default { name: 'App', data() { return { - appId: 'ww38152475d1bca752', - agentId: '1000003', + appId: 'appId', + agentId: '1000005', } }, created() { diff --git a/linkwe-mobile/src/api/chat.js b/linkwe-mobile/src/api/chat.js index 5cf50bfaa..d04bf93ae 100644 --- a/linkwe-mobile/src/api/chat.js +++ b/linkwe-mobile/src/api/chat.js @@ -15,12 +15,10 @@ export function getTypeList() { * 素材列表 * @param {*} sideId */ -export function getMaterialList(sideId) { +export function getMaterialList(params) { return request({ url: service + '/item/list', - params: { - sideId, - }, + params, }) } @@ -28,12 +26,10 @@ export function getMaterialList(sideId) { * 收藏列表(h5我的) * @param {*} userId */ -export function getCollectionList(userId) { +export function getCollectionList(params) { return request({ url: service + '/collection/list', - params: { - userId, - }, + params, }) } @@ -54,7 +50,7 @@ export function addCollection(data) { } /** - * 侧边栏抓取素材 + * 取消收藏 * @param {*} data * { materialId:素材id @@ -64,7 +60,7 @@ userId:用户id export function cancleCollection(data) { return request({ url: service + '/collection/cancleCollection', - method: 'put', + method: 'post', data, }) } diff --git a/linkwe-mobile/src/api/common.js b/linkwe-mobile/src/api/common.js index c5609d7f5..1f9c557ae 100644 --- a/linkwe-mobile/src/api/common.js +++ b/linkwe-mobile/src/api/common.js @@ -1,6 +1,6 @@ import request from '@/utils/request' import config from '@/config' -const service = config.services.wecom + '/ticket' +const service = config.services.wecom /** * 获取应用的jsapi_ticket @@ -8,7 +8,7 @@ const service = config.services.wecom + '/ticket' */ export function getAgentTicket(url) { return request({ - url: service + '/getAgentTicket', + url: service + '/ticket/getAgentTicket', params: { url, }, @@ -21,7 +21,7 @@ export function getAgentTicket(url) { */ export function getAppTicket(url) { return request({ - url: service + '/getAppTicket', + url: service + '/ticket/getAppTicket', params: { url, }, @@ -32,8 +32,11 @@ export function getAppTicket(url) { * 获取企业的jsapi_ticket * @param {*} url 页面url */ -export function getLoginUserId() { +export function getUserInfo(code) { return request({ - url: '/system/user/findCurrentLoginUser', + url: service + '/user/getUserInfo', + params: { + code, + }, }) } diff --git a/linkwe-mobile/src/utils/request.js b/linkwe-mobile/src/utils/request.js index b997528d5..17a51e34e 100644 --- a/linkwe-mobile/src/utils/request.js +++ b/linkwe-mobile/src/utils/request.js @@ -1,4 +1,5 @@ import axios from 'axios' +import { Toast } from 'vant' function createAxios(baseURL) { const instance = axios.create({ @@ -44,10 +45,7 @@ function createAxios(baseURL) { if (error.response) { addErrorLog(error.response) } else { - // Message.error({ - // content: `服务器未启动或连接超时`, - // duration: 3 - // }) + Toast.fail('服务器未启动或连接超时') console.error('服务器未启动或连接超时') } return Promise.reject(error) @@ -71,6 +69,7 @@ function createAxios(baseURL) { process.env.NODE_ENV === 'development' ? console.error(`错误: 路径: ${responseURL}, 返回值 : ${responseText}`) : console.error(`${JSON.parse(responseText).message}`) + Toast.fail(`${responseText}`) } return instance diff --git a/linkwe-mobile/src/views/chat/List.vue b/linkwe-mobile/src/views/chat/List.vue index 1662046a8..0c66ed2d0 100644 --- a/linkwe-mobile/src/views/chat/List.vue +++ b/linkwe-mobile/src/views/chat/List.vue @@ -16,38 +16,55 @@ export default { type: String, default: '', }, + keyword: { + type: String, + default: '', + }, }, data() { return { - list: [{}], loading: false, finished: false, + error: false, + pageSize: 1, + pageNum: 10, + list: [], collectList: [], } }, watch: {}, computed: {}, - created() {}, + created() { + this.getList() + this.userId && this.getCollectionList() + }, mounted() {}, methods: { - getList() { + getList(page) { + let pageSize = page || this.pageSize++ + let pageNum = this.pageNum + let keyword = this.keyword + page == 1 && (this.list = []) this.loading = true ;(this.sideId - ? getMaterialList(this.sideId) - : getCollectionList(this.userId) + ? getMaterialList({ sideId: this.sideId, pageSize, pageNum, keyword }) + : getCollectionList({ userId: this.userId, pageSize, pageNum, keyword }) ) .then(({ rows, total }) => { - this.list = rows + this.list.push(...rows) this.loading = false - this.finished = true + // 数据全部加载完成 + if (this.list.length >= total) { + this.finished = true + } }) .catch(() => { - this.loading = false - this.finished = true + this.error = true }) }, getCollectionList() { - getCollectionList().then(({ rows, total }) => { + let data = { userId: this.userId, pageSize: 1, pageNum: 1000 } + getCollectionList(data).then(({ rows, total }) => { this.collectList = rows.map((d) => d.id) }) }, @@ -117,10 +134,12 @@ export default { }, collect(materialId) { ;(this.isCollected(materialId) ? cancleCollection : addCollection)({ - userId, + userId: this.userId, materialId, }).then(({ data }) => { - this.isCollected.splice(this.isCollected.indexOf(materialId), 1) + this.isCollected(materialId) + ? this.isCollected.splice(this.isCollected.indexOf(materialId), 1) + : this.isCollected.push(materialId) }) }, }, @@ -133,7 +152,9 @@ export default { v-model="loading" :finished="finished" finished-text="没有更多了" - @load="getList" + :error.sync="error" + error-text="请求失败,点击重新加载" + @load="getList()" >
{{ item.content }}
@@ -143,7 +164,7 @@ export default {
发送
-
+
{{ isCollected(item.id) ? '已' : '' }}收藏
diff --git a/linkwe-mobile/src/views/chat/index.vue b/linkwe-mobile/src/views/chat/index.vue index 2c15fa13e..bcd446bf2 100644 --- a/linkwe-mobile/src/views/chat/index.vue +++ b/linkwe-mobile/src/views/chat/index.vue @@ -1,5 +1,6 @@ +2 -- Gitee From 1920600b39da23d6ef9a86bcd76dfd0ed070c640 Mon Sep 17 00:00:00 2001 From: qiyeqing <444251443@qq.com> Date: Mon, 25 Jan 2021 10:52:18 +0800 Subject: [PATCH 054/177] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linkwe-ui/src/views/conversation/page/userTest.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/linkwe-ui/src/views/conversation/page/userTest.vue b/linkwe-ui/src/views/conversation/page/userTest.vue index 85e972990..28345ad04 100644 --- a/linkwe-ui/src/views/conversation/page/userTest.vue +++ b/linkwe-ui/src/views/conversation/page/userTest.vue @@ -47,8 +47,9 @@
-
与{{chatData.finalChatContext.fromInfo.name}} 的聊天 +
+ 下载会话
-- Gitee From 1fe5c701593c55b94408753713e147fcaf52a99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A7=9A=E5=AE=87=E8=88=AA?= <45991359+Su57@users.noreply.github.com> Date: Mon, 25 Jan 2021 11:14:37 +0800 Subject: [PATCH 055/177] =?UTF-8?q?=E7=BE=A4=E6=B4=BB=E7=A0=81=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/system/SysLoginController.java | 29 ++ .../wecom/WeGroupCodeActualController.java | 101 ++++--- .../wecom/WeGroupCodeController.java | 167 ++++++++--- .../common/constant/WeConstans.java | 13 + .../com/linkwechat/common/utils/QREncode.java | 273 ++++++++++++++---- .../common/utils/file/FileUploadUtils.java | 2 +- .../linkwechat/wecom/domain/WeGroupCode.java | 78 ++++- .../wecom/domain/WeGroupCodeActual.java | 16 +- .../impl/WeEventChangeExternalChatImpl.java | 42 +++ .../wecom/mapper/WeGroupCodeActualMapper.java | 46 ++- .../wecom/mapper/WeGroupCodeMapper.java | 14 + .../service/IWeGroupCodeActualService.java | 93 +++--- .../wecom/service/IWeGroupCodeService.java | 97 ++++--- .../impl/WeGroupCodeActualServiceImpl.java | 159 ++++++---- .../service/impl/WeGroupCodeServiceImpl.java | 207 +++++++++---- .../mapper/wecom/WeEmpleCodeMapper.xml | 2 +- .../mapper/wecom/WeGroupCodeActualMapper.xml | 40 ++- .../mapper/wecom/WeGroupCodeMapper.xml | 120 ++++++-- sql/link-wechat.sql | 18 +- 19 files changed, 1130 insertions(+), 387 deletions(-) create mode 100644 linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysLoginController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysLoginController.java index b9bdd43d1..53304c6bc 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysLoginController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/system/SysLoginController.java @@ -52,6 +52,8 @@ public class SysLoginController @Autowired private WeAccessTokenClient weAccessTokenClient; +// @Autowired +// private IWeGroupCodeService weGroupCodeService; /** @@ -157,5 +159,32 @@ public class SysLoginController } +// /** +// * 从群活码获取第一个可用的实际码 +// */ +// @GetMapping("/getChatQRCode") +// public AjaxResult getActual(Long weGroupCodeId) { +// WeGroupCode weGroupCode = weGroupCodeService.selectWeGroupCodeById(weGroupCodeId); +// List weGroupCodeActualList = weGroupCodeService.selectActualListByGroupCodeId(weGroupCodeId); +// WeGroupCodeActual weGroupCodeActual = null; +// for (WeGroupCodeActual item : weGroupCodeActualList) { +// // 获取第一个可用的实际码 +// if (item.getStatus().intValue() == WeConstans.WE_GROUP_CODE_ENABLE) { +// weGroupCodeActual = item; +// break; +// } +// } +// if (StringUtils.isNotNull(weGroupCodeActual)) { +// AjaxResult ajax = AjaxResult.success(); +// ajax.put("tipMsg", weGroupCode.getTipMsg()); +// ajax.put("activityName", weGroupCode.getActivityName()); +// ajax.put("groupName", weGroupCodeActual.getChatGroupName()); +// ajax.put("actualQRCode", weGroupCodeActual.getActualGroupQrCode()); +// return ajax; +// } else { +// return AjaxResult.error(HttpStatus.NOT_FOUND, "数据不存在"); +// } +// } + } diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeActualController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeActualController.java index 0398f284b..d0cb07bf0 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeActualController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeActualController.java @@ -1,39 +1,43 @@ package com.linkwechat.web.controller.wecom; import com.linkwechat.common.annotation.Log; +import com.linkwechat.common.constant.HttpStatus; import com.linkwechat.common.core.controller.BaseController; import com.linkwechat.common.core.domain.AjaxResult; +import com.linkwechat.common.core.page.TableDataInfo; import com.linkwechat.common.enums.BusinessType; +import com.linkwechat.common.utils.StringUtils; import com.linkwechat.wecom.domain.WeGroupCodeActual; import com.linkwechat.wecom.service.IWeGroupCodeActualService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + /** * 实际群码Controller - * + * * @author ruoyi * @date 2020-10-07 */ @RestController @RequestMapping("/wecom/actual") -public class WeGroupCodeActualController extends BaseController -{ +public class WeGroupCodeActualController extends BaseController { @Autowired private IWeGroupCodeActualService weGroupCodeActualService; -// /** -// * 查询实际群码列表 -// */ -// @PreAuthorize("@ss.hasPermi('wecom:actual:list')") -// @GetMapping("/list") -// public TableDataInfo list(WeGroupCodeActual weGroupCodeActual) -// { -// startPage(); -// List list = weGroupCodeActualService.selectWeGroupCodeActualList(weGroupCodeActual); -// return getDataTable(list); -// } + /** + * 查询实际群码列表 + */ + @PreAuthorize("@ss.hasPermi('wecom:actual:list')") + @GetMapping("/list") + public TableDataInfo list(WeGroupCodeActual weGroupCodeActual) { + startPage(); + List list = weGroupCodeActualService.selectWeGroupCodeActualList(weGroupCodeActual); + return getDataTable(list); + } // // /** // * 导出实际群码列表 @@ -47,27 +51,32 @@ public class WeGroupCodeActualController extends BaseController // ExcelUtil util = new ExcelUtil(WeGroupCodeActual.class); // return util.exportExcel(list, "actual"); // } -// -// /** -// * 获取实际群码详细信息 -// */ -// @PreAuthorize("@ss.hasPermi('wecom:actual:query')") -// @GetMapping(value = "/{id}") -// public AjaxResult getInfo(@PathVariable("id") Long id) -// { -// return AjaxResult.success(weGroupCodeActualService.selectWeGroupCodeActualById(id)); -// } /** - * 新增实际群码 + * 获取实际群码详细信息 + */ + @PreAuthorize("@ss.hasPermi('wecom:actual:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) { + WeGroupCodeActual weGroupCodeActual = weGroupCodeActualService.selectWeGroupCodeActualById(id); + if (StringUtils.isNull(weGroupCodeActual)) { + return AjaxResult.error(HttpStatus.NOT_FOUND, "数据不存在"); + } + return AjaxResult.success(weGroupCodeActual); + } + + /** + * 新增实际群码查询 */ @PreAuthorize("@ss.hasPermi('wecom:actual:add')") @Log(title = "实际群码", businessType = BusinessType.INSERT) @PostMapping - public AjaxResult add(@RequestBody WeGroupCodeActual weGroupCodeActual) - { - weGroupCodeActualService.save(weGroupCodeActual); - return AjaxResult.success(); + public AjaxResult add(@Validated @RequestBody WeGroupCodeActual weGroupCodeActual) { + // 唯一性检查 + if (!weGroupCodeActualService.checkChatIdUnique(weGroupCodeActual)) { + return AjaxResult.error("新增实际群码失败, 该群聊二维码已存在"); + } + return toAjax(weGroupCodeActualService.insertWeGroupCodeActual(weGroupCodeActual)); } /** @@ -76,20 +85,26 @@ public class WeGroupCodeActualController extends BaseController @PreAuthorize("@ss.hasPermi('wecom:actual:edit')") @Log(title = "实际群码", businessType = BusinessType.UPDATE) @PutMapping - public AjaxResult edit(@RequestBody WeGroupCodeActual weGroupCodeActual) - { - weGroupCodeActualService.updateById(weGroupCodeActual); - return AjaxResult.success(); + public AjaxResult edit(@RequestBody WeGroupCodeActual weGroupCodeActual) { + WeGroupCodeActual original = weGroupCodeActualService.selectWeGroupCodeActualById(weGroupCodeActual.getId()); + if (StringUtils.isNull(original)) { + return AjaxResult.error(HttpStatus.NOT_FOUND, "数据不存在"); + } + // 实际码对应客户群变化时,检查其唯一性 + if (!original.getChatId().equals(weGroupCodeActual.getChatId()) && + !weGroupCodeActualService.checkChatIdUnique(weGroupCodeActual)) { + return AjaxResult.error("修改实际群码失败, 该群聊二维码已存在"); + } + return toAjax(weGroupCodeActualService.updateWeGroupCodeActual(weGroupCodeActual)); } -// /** -// * 删除实际群码 -// */ -// @PreAuthorize("@ss.hasPermi('wecom:actual:remove')") -// @Log(title = "实际群码", businessType = BusinessType.DELETE) -// @DeleteMapping("/{ids}") -// public AjaxResult remove(@PathVariable Long[] ids) -// { -// return toAjax(weGroupCodeActualService.deleteWeGroupCodeActualByIds(ids)); -// } + /** + * 删除实际群码 + */ + @PreAuthorize("@ss.hasPermi('wecom:actual:remove')") + @Log(title = "实际群码", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) { + return toAjax(weGroupCodeActualService.deleteWeGroupCodeActualByIds(ids)); + } } diff --git a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeController.java b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeController.java index 76bd27428..2f77bd264 100644 --- a/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeController.java +++ b/linkwe-admin/src/main/java/com/linkwechat/web/controller/wecom/WeGroupCodeController.java @@ -1,41 +1,109 @@ package com.linkwechat.web.controller.wecom; +import cn.hutool.core.collection.CollectionUtil; import com.linkwechat.common.annotation.Log; +import com.linkwechat.common.constant.HttpStatus; import com.linkwechat.common.core.controller.BaseController; import com.linkwechat.common.core.domain.AjaxResult; +import com.linkwechat.common.core.page.TableDataInfo; import com.linkwechat.common.enums.BusinessType; +import com.linkwechat.common.utils.SecurityUtils; +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.common.utils.file.FileUtils; import com.linkwechat.wecom.domain.WeGroupCode; +import com.linkwechat.wecom.domain.WeGroupCodeActual; import com.linkwechat.wecom.service.IWeGroupCodeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + /** * 客户群活码Controller - * + * * @author ruoyi * @date 2020-10-07 */ @RestController @RequestMapping("/wecom/groupCode") -public class WeGroupCodeController extends BaseController -{ +public class WeGroupCodeController extends BaseController { @Autowired private IWeGroupCodeService weGroupCodeService; -// /** -// * 查询客户群活码列表 -// */ -// @PreAuthorize("@ss.hasPermi('wecom:code:list')") -// @GetMapping("/list") -// public TableDataInfo list(WeGroupCode weGroupCode) -// { -// startPage(); -// List list = weGroupCodeService.selectWeGroupCodeList(weGroupCode); -// return getDataTable(list); -// } -// + /** + * 查询客户群活码列表 + */ + @PreAuthorize("@ss.hasPermi('wecom:groupCode:list')") + @GetMapping("/list") + public TableDataInfo list(WeGroupCode weGroupCode) { + startPage(); + List list = weGroupCodeService.selectWeGroupCodeList(weGroupCode); + return getDataTable(list); + } + + /** + * 批量下载群活码 + */ + @PreAuthorize("@ss.hasPermi('wecom:groupCode:downloadBatch')") + @Log(title = "群活码批量下载", businessType = BusinessType.OTHER) + @GetMapping("/downloadBatch") + public void downloadBatch(String ids, HttpServletRequest request, HttpServletResponse response) { + List idList = Arrays.stream(Optional.ofNullable(ids).orElse("").split(",")) + .filter(StringUtils::isNotEmpty).collect(Collectors.toList()); + try { + List weGroupCodeList = weGroupCodeService.selectWeGroupCodeListByIds(idList); + ZipOutputStream zos = new ZipOutputStream(response.getOutputStream()); + if (CollectionUtil.isNotEmpty(weGroupCodeList)) { + for (WeGroupCode weGroupCode : weGroupCodeList) { + String codeUrl = weGroupCode.getCodeUrl(); + if (StringUtils.isEmpty(codeUrl)) { + continue; + } + URL url = new URL(codeUrl); + String fileName = weGroupCode.getActivityName() + ".png"; + zos.putNextEntry(new ZipEntry(fileName)); + InputStream fis = url.openConnection().getInputStream(); + byte[] buffer = new byte[1024]; + int r = 0; + while ((r = fis.read(buffer)) != -1) { + zos.write(buffer, 0, r); + } + fis.close(); + } + } + //关闭zip输出流 + zos.flush(); + zos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @PreAuthorize("@ss.hasPermi('wecom:groupCode:download')") + @Log(title = "群活码下载", businessType = BusinessType.OTHER) + @GetMapping("/download") + public void download(String id, HttpServletRequest request, HttpServletResponse response) { + WeGroupCode weGroupCode = weGroupCodeService.selectWeGroupCodeById(Long.valueOf(id)); + try { + FileUtils.downloadFile(weGroupCode.getCodeUrl(), response.getOutputStream()); + } catch (IOException e) { + e.printStackTrace(); + } + } + // /** // * 导出客户群活码列表 // */ @@ -49,39 +117,68 @@ public class WeGroupCodeController extends BaseController // return util.exportExcel(list, "code"); // } -// /** -// * 获取客户群活码详细信息 -// */ -// @PreAuthorize("@ss.hasPermi('wecom:code:query')") -// @GetMapping(value = "/{id}") -// public AjaxResult getInfo(@PathVariable("id") Long id) -// { -// return AjaxResult.success(weGroupCodeService.selectWeGroupCodeById(id)); -// } + /** + * 获取客户群活码详细信息 + */ + @PreAuthorize("@ss.hasPermi('wecom:groupCode:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) { + WeGroupCode weGroupCode = weGroupCodeService.selectWeGroupCodeById(id); + if (StringUtils.isNull(weGroupCode)) { + return AjaxResult.error(HttpStatus.NOT_FOUND, "数据不存在"); + } + List weGroupCodeActualList = weGroupCodeService.selectActualListByGroupCodeId(weGroupCode.getId()); + weGroupCode.setActualList(weGroupCodeActualList); + return AjaxResult.success(weGroupCode); + } /** * 新增客户群活码 */ - @PreAuthorize("@ss.hasPermi('wecom:code:add')") + @PreAuthorize("@ss.hasPermi('wecom:groupCode:add')") @Log(title = "客户群活码", businessType = BusinessType.INSERT) @PostMapping - public AjaxResult add(@RequestBody WeGroupCode weGroupCode) - { + public AjaxResult add(@Validated @RequestBody WeGroupCode weGroupCode) { + // 唯一性检查 + if (!weGroupCodeService.checkActivityNameUnique(weGroupCode)) { + return AjaxResult.error("添加群活码失败,活码名称 " + weGroupCode.getActivityName() + " 已存在"); + } + AjaxResult ajax = AjaxResult.success(); + weGroupCode.setCreateBy(SecurityUtils.getUsername()); weGroupCodeService.insertWeGroupCode(weGroupCode); - return AjaxResult.success(); + ajax.put("id", weGroupCode.getId()); + return ajax; } /** * 修改客户群活码 */ - @PreAuthorize("@ss.hasPermi('wecom:code:edit')") + @PreAuthorize("@ss.hasPermi('wecom:groupCode:edit')") @Log(title = "客户群活码", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@RequestBody WeGroupCode weGroupCode) - { - weGroupCodeService.updateWeGroupCode(weGroupCode); + @PutMapping(value = "/{id}") + public AjaxResult edit(@PathVariable("id") Long id, @RequestBody WeGroupCode weGroupCode) { + WeGroupCode originalCode = weGroupCodeService.selectWeGroupCodeById(id); + if (StringUtils.isNull(originalCode)) { + return AjaxResult.error(HttpStatus.NOT_FOUND, "数据不存在"); + } + // 唯一性检查 + if (!originalCode.getActivityName().equals(weGroupCode.getActivityName()) && + !weGroupCodeService.checkActivityNameUnique(weGroupCode)) { + return AjaxResult.error("修改群活码失败,活码名称 " + weGroupCode.getActivityName() + " 已存在"); + } + weGroupCode.setId(id); + weGroupCode.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(weGroupCodeService.updateWeGroupCode(weGroupCode)); + } - return AjaxResult.success(); + /** + * 删除客户群活码 + */ + @PreAuthorize("@ss.hasPermi('wecom:groupCode:remove')") + @Log(title = "客户群活码", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult batchRemove(@PathVariable Long[] ids) { + return toAjax(weGroupCodeService.deleteWeGroupCodeByIds(ids)); } // /** @@ -94,4 +191,6 @@ public class WeGroupCodeController extends BaseController // { // return toAjax(weGroupCodeService.deleteWeGroupCodeByIds(ids)); // } + + } diff --git a/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java b/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java index 594b6c898..e031500de 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/constant/WeConstans.java @@ -105,6 +105,16 @@ public class WeConstans { */ public static final Integer WE_ROOT_CATEGORY_ID = 0; + /** + * 实际群活码正在使用中 + */ + public static final Integer WE_GROUP_CODE_ENABLE = 0; + + /** + * 群活码已禁用/达到扫码次数上限 + */ + public static final Integer WE_GROUP_CODE_DISABLE = 2; + /** * 单人活码 @@ -240,6 +250,9 @@ public class WeConstans { put("batch_job_result", "weEventBatchJobResultImpl"); //外部联系人事件 put("change_external_contact", "weEventChangeExternalContactImpl"); + //客户群聊事件 + put("change_external_chat", "weEventChangeExternalChatImpl"); + } }; diff --git a/linkwe-common/src/main/java/com/linkwechat/common/utils/QREncode.java b/linkwe-common/src/main/java/com/linkwechat/common/utils/QREncode.java index 571fa32a2..7615d0a16 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/utils/QREncode.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/utils/QREncode.java @@ -1,66 +1,241 @@ package com.linkwechat.common.utils; +import java.awt.*; +import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.net.URL; +import java.util.HashMap; import java.util.Hashtable; import javax.imageio.ImageIO; +import javax.servlet.ServletOutputStream; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.linkwechat.common.constant.Constants; + public class QREncode { - - private static final int BLACK = 0xFF000000; - private static final int WHITE = 0xFFFFFFFF; - - private QREncode() {} - - - public static BufferedImage toBufferedImage(BitMatrix matrix) { - int width = matrix.getWidth(); - int height = matrix.getHeight(); - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); - } - } - return image; - } - - - public static void writeToFile(BitMatrix matrix, String format, File file) - throws IOException { - BufferedImage image = toBufferedImage(matrix); - if (!ImageIO.write(image, format, file)) { - throw new IOException("Could not write an image of format " + format + " to " + file); - } - } - - - public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) - throws IOException { - BufferedImage image = toBufferedImage(matrix); - if (!ImageIO.write(image, format, stream)) { - throw new IOException("Could not write an image of format " + format); - } - } - @SuppressWarnings({ "unchecked", "rawtypes" }) -public static void main(String[] args) throws Exception { - String text = "http://open.weixin.qq.com/qr/code/?username=LinkWeChat2021"; - int width = 300; - int height = 300; - String format = "png"; - Hashtable hints = new Hashtable(); - hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); - BitMatrix bitMatrix = new MultiFormatWriter().encode(text, - BarcodeFormat.QR_CODE, width, height, hints); - File outputFile = new File("d:"+File.separator+"qr.png"); - QREncode.writeToFile(bitMatrix, format, outputFile); - } + + private static final int BLACK = 0xFF000000; + private static final int WHITE = 0xFFFFFFFF; + /** + * 默认宽度 + */ + private static final Integer WIDTH = 140; + /** + * 默认高度 + */ + private static final Integer HEIGHT = 140; + + /** + * LOGO 默认宽度 + */ + private static final Integer LOGO_WIDTH = 22; + /** + * LOGO 默认高度 + */ + private static final Integer LOGO_HEIGHT = 22; + + /** + * 图片格式 + */ + private static final String IMAGE_FORMAT = "png"; + + + private QREncode() { + } + + + public static BufferedImage toBufferedImage(BitMatrix matrix) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); + } + } + return image; + } + + public static void writeToFile(BitMatrix matrix, String format, File file) + throws IOException { + BufferedImage image = toBufferedImage(matrix); + if (!ImageIO.write(image, format, file)) { + throw new IOException("Could not write an image of format " + format + " to " + file); + } + } + + public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) + throws IOException { + BufferedImage image = toBufferedImage(matrix); + if (!ImageIO.write(image, format, stream)) { + throw new IOException("Could not write an image of format " + format); + } + } + + /** + * 生成二维码 + * + * @param content 内容 + * @param logoUrl logo 在线地址 + * @return + */ + private static BufferedImage crateQRCode(String content, String logoUrl) { + if (StringUtils.isNotBlank(content)) { + ServletOutputStream stream = null; + HashMap hints = new HashMap<>(4); + // 指定字符编码为utf-8 + hints.put(EncodeHintType.CHARACTER_SET, Constants.UTF8); + // 指定二维码的纠错等级为中级 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + // 设置图片的边距 + hints.put(EncodeHintType.MARGIN, 2); + try { + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints); + BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + for (int x = 0; x < WIDTH; x++) { + for (int y = 0; y < HEIGHT; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); + } + } + if (StringUtils.isNotBlank(logoUrl)) { + insertLogo(bufferedImage, logoUrl); + } + return bufferedImage; + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (stream != null) { + try { + stream.flush(); + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + return null; + } + + /** + * 二维码插入logo + * + * @param source 二维码 + * @param logoUrl logo 在线地址 + * @throws Exception + */ + private static void insertLogo(BufferedImage source, String logoUrl) throws Exception { + // logo 源可为 File/InputStream/URL + Image src = ImageIO.read(new URL(logoUrl)); + // 插入LOGO + Graphics2D graph = source.createGraphics(); + int x = (WIDTH - LOGO_WIDTH) / 2; + int y = (HEIGHT - LOGO_HEIGHT) / 2; + graph.drawImage(src, x, y, LOGO_WIDTH, LOGO_HEIGHT, null); + Shape shape = new RoundRectangle2D.Float(x, y, LOGO_WIDTH, LOGO_HEIGHT, 6, 6); + graph.setStroke(new BasicStroke(3f)); + graph.draw(shape); + graph.dispose(); + } + + /** + * 生成二维码并保存 + * + * @param content 内容 + * @throws IOException + */ + public static void saveQRCode(String content, String filePath) throws IOException { + BufferedImage image = crateQRCode(content, null); + if (StringUtils.isNotNull(image)) { + File file = createFile(filePath); + ImageIO.write(image, IMAGE_FORMAT, file); + } + } + + /** + * 生成带logo的二维码并保存 + * + * @param content 二维码内容 + * @param logoUrl logo地址 + * @throws IOException + */ + public static void saveQRCode(String content, String logoUrl, String filePath) throws IOException { + BufferedImage image = crateQRCode(content, logoUrl); + if (StringUtils.isNotNull(image)) { + File file = createFile(filePath); + ImageIO.write(image, IMAGE_FORMAT, file); + } + } + + /** + * 生成带二维码并生成流文件进行传输 + * + * @param content 内容 + * @param output 输出流 + * @throws Exception + */ + public void getQRCode(String content, OutputStream output) throws Exception { + BufferedImage image = crateQRCode(content, null); + if (StringUtils.isNotNull(image)) { + ImageIO.write(image, IMAGE_FORMAT, output); + } + } + + /** + * 生成带logo的二维码并生成流文件进行传输 + * + * @param content 内容 + * @param logoUrl logo资源 + * @param output 输出流 + * @throws Exception + */ + public void getQRCode(String content, String logoUrl, OutputStream output) throws Exception { + BufferedImage image = crateQRCode(content, logoUrl); + if (StringUtils.isNotNull(image)) { + ImageIO.write(image, IMAGE_FORMAT, output); + } + } + + /** + * 根据指定路径创建空文件 + * @param path 路径 + * @return 空文件 + */ + private static File createFile(String path) { + File file = new File(path); + try { + if (!file.exists()) { + file.getParentFile().mkdir(); + file.createNewFile(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return file; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void main(String[] args) throws Exception { + String text = "http://open.weixin.qq.com/qr/code/?username=LinkWeChat2021"; + int width = 300; + int height = 300; + String format = "png"; + Hashtable hints = new Hashtable<>(); + hints.put(EncodeHintType.CHARACTER_SET, Constants.UTF8); + BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints); + File outputFile = new File("d:" + File.separator + "qr.png"); + QREncode.writeToFile(bitMatrix, format, outputFile); + } } diff --git a/linkwe-common/src/main/java/com/linkwechat/common/utils/file/FileUploadUtils.java b/linkwe-common/src/main/java/com/linkwechat/common/utils/file/FileUploadUtils.java index 438ea456d..e709070da 100644 --- a/linkwe-common/src/main/java/com/linkwechat/common/utils/file/FileUploadUtils.java +++ b/linkwe-common/src/main/java/com/linkwechat/common/utils/file/FileUploadUtils.java @@ -158,7 +158,7 @@ public class FileUploadUtils { return desc; } - private static final String getPathFileName(String uploadDir, String fileName) throws IOException { + public static final String getPathFileName(String uploadDir, String fileName) throws IOException { int dirLastIndex = RuoYiConfig.getProfile().length() + 1; String currentDir = StringUtils.substring(uploadDir, dirLastIndex); String pathFileName; diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCode.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCode.java index 47848d036..b9dad9cd1 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCode.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCode.java @@ -6,51 +6,103 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.linkwechat.common.core.domain.BaseEntity; import com.linkwechat.common.utils.SnowFlakeUtil; import lombok.Data; +import lombok.EqualsAndHashCode; +import javax.validation.constraints.NotNull; import java.util.List; /** * 客户群活码对象 we_group_code - * + * * @author ruoyi * @date 2020-10-07 */ @Data +@EqualsAndHashCode(callSuper = true) @TableName("we_group_code") -public class WeGroupCode -{ +public class WeGroupCode extends BaseEntity { private static final long serialVersionUID = 1L; - /** $column.columnComment */ + /** + * $column.columnComment + */ @TableId - private Long id= SnowFlakeUtil.nextId(); + private Long id = SnowFlakeUtil.nextId(); - /** 活动头像 */ + /** + * 活码URL + */ + private String codeUrl; + + /** + * 活码头像 + */ private String activityHeadUrl; - /** 活动名称 */ + /** + * 活码名称 + */ + @NotNull(message = "活码名称不能为空") private String activityName; - /** 场景 */ + /** + * 活码描述 + */ + @NotNull(message = "活码描述不能为空") + private String activityDesc; + + /** + * 场景 + */ private String activityScene; - /** 引导语 */ + /** + * 加群引导语 + */ private String guide; - /** 进群是否提示:1:是;0:否; */ + /** + * 进群是否提示:1:是;0:否; + */ private Long joinGroupIsTip; - /** 进群提示语 */ + /** + * 进群提示语 + */ private String tipMsg; - /** 客服二维码 */ + /** + * 客服二维码 + */ private String customerServerQrCode; - /** 0:正常;2:删除; */ + /** + * 0:正常;2:删除; + */ private Long delFlag; + /** + * 可用实际码数量 + */ + @TableField(exist = false) + private Long availableCodes; + + /** + * 实际码扫码次数之和 + */ + @TableField(exist = false) + private Long totalScanTimes; + + /** + * 即将过期实际码数量 + */ + @TableField(exist = false) + private Long aboutToExpireCodes; + /** + * 实际码 + */ @TableField(exist = false) private List actualList; diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCodeActual.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCodeActual.java index b7b9e39a6..88678a008 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCodeActual.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/domain/WeGroupCodeActual.java @@ -5,10 +5,11 @@ import java.util.Date; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; -import com.linkwechat.common.core.domain.BaseEntity; import com.linkwechat.common.utils.SnowFlakeUtil; import lombok.Data; +import javax.validation.constraints.NotNull; + /** * 实际群码对象 we_group_code_actual @@ -27,21 +28,32 @@ public class WeGroupCodeActual private Long id=SnowFlakeUtil.nextId(); /** 实际群码 */ + @NotNull(message = "实际群活码称不能为空") private String actualGroupQrCode; /** 群名称 */ + @NotNull(message = "群名称不能为空") private String groupName; /** 有效期 */ + @NotNull(message = "有效期不能为空") @JsonFormat(pattern = "yyyy-MM-dd") private Date effectTime; /** 扫码次数限制 */ + @NotNull(message = "次数限制不能为空") private Long scanCodeTimesLimit; /** 群活码id */ + @NotNull(message = "群活码id不能为空") private Long groupCodeId; + /** 客户群id */ + private String chatId; + + /** 客户群名称 */ + private String chatGroupName; + /** 扫码次数 */ private Long scanCodeTimes; @@ -50,6 +62,4 @@ public class WeGroupCodeActual /** 0:使用中 */ private Long status; - - } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java new file mode 100644 index 000000000..2cf02e49d --- /dev/null +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/factory/impl/WeEventChangeExternalChatImpl.java @@ -0,0 +1,42 @@ +package com.linkwechat.wecom.factory.impl; + +import com.linkwechat.common.constant.WeConstans; +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.wecom.domain.WeGroupCodeActual; +import com.linkwechat.wecom.domain.vo.WxCpXmlMessageVO; +import com.linkwechat.wecom.factory.WeCallBackEventFactory; +import com.linkwechat.wecom.service.IWeGroupCodeActualService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 客户群变更回调事件处理 + */ +@Service +@Slf4j +public class WeEventChangeExternalChatImpl implements WeCallBackEventFactory { + + @Autowired + private IWeGroupCodeActualService weGroupCodeActualService; + + @Override + public void eventHandle(WxCpXmlMessageVO message) { + String changeType = message.getChangeType(); + // 客户群状态变更(群名变更,群成员增加或移除,群主变更,群公告变更) + if (changeType.equals("update")) { + WeGroupCodeActual weGroupCodeActual = weGroupCodeActualService.selectActualCodeByChatId(message.getChatId()); + if (StringUtils.isNotNull(weGroupCodeActual)) + { + // 扫码次数+1 + weGroupCodeActual.setScanCodeTimes(weGroupCodeActual.getScanCodeTimes() + 1); + if (weGroupCodeActual.getScanCodeTimes().equals(weGroupCodeActual.getScanCodeTimesLimit())) + { + weGroupCodeActual.setStatus(WeConstans.WE_GROUP_CODE_DISABLE.longValue()); + log.info("客户群 " + weGroupCodeActual.getGroupName() + " 二维码扫描次数已达上限"); + } + weGroupCodeActualService.updateWeGroupCodeActual(weGroupCodeActual); + } + } + } +} diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeActualMapper.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeActualMapper.java index f45735425..5be00c8e7 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeActualMapper.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeActualMapper.java @@ -7,23 +7,29 @@ import com.linkwechat.wecom.domain.WeGroupCodeActual; /** * 实际群码Mapper接口 - * + * * @author ruoyi * @date 2020-10-07 */ -public interface WeGroupCodeActualMapper extends BaseMapper -{ +public interface WeGroupCodeActualMapper extends BaseMapper { /** * 查询实际群码 - * + * * @param id 实际群码ID * @return 实际群码 */ public WeGroupCodeActual selectWeGroupCodeActualById(Long id); + /** + * 根据群聊id获取群实际码 + * @param chatId 群聊id + * @return 结果 + */ + public WeGroupCodeActual selectWeGroupCodeActualByChatId(String chatId); + /** * 查询实际群码列表 - * + * * @param weGroupCodeActual 实际群码 * @return 实际群码集合 */ @@ -31,7 +37,7 @@ public interface WeGroupCodeActualMapper extends BaseMapper /** * 新增实际群码 - * + * * @param weGroupCodeActual 实际群码 * @return 结果 */ @@ -39,7 +45,7 @@ public interface WeGroupCodeActualMapper extends BaseMapper /** * 修改实际群码 - * + * * @param weGroupCodeActual 实际群码 * @return 结果 */ @@ -47,7 +53,7 @@ public interface WeGroupCodeActualMapper extends BaseMapper /** * 删除实际群码 - * + * * @param id 实际群码ID * @return 结果 */ @@ -55,9 +61,31 @@ public interface WeGroupCodeActualMapper extends BaseMapper /** * 批量删除实际群码 - * + * * @param ids 需要删除的数据ID * @return 结果 */ public int deleteWeGroupCodeActualByIds(Long[] ids); + + /** + * 通过群活码id查询实际码列表 + * + * @param groupCodeId 群活码id + * @return 结果 + */ + public List selectActualListByGroupCodeId(Long groupCodeId); + + /** + * 通过群活码id删除实际码 + * @param groupCodeIds 群活码id列表 + * @return 结果 + */ + public int deleteActualListByGroupCodeIds(Long[] groupCodeIds); + + /** + * 检测chatId是否唯一 + * @param chatId 群聊id + * @return 结果 + */ + public int checkChatIdUnique(String chatId); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeMapper.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeMapper.java index 356c7c642..9a70d0ad9 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeMapper.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/mapper/WeGroupCodeMapper.java @@ -29,6 +29,13 @@ public interface WeGroupCodeMapper extends BaseMapper */ public List selectWeGroupCodeList(WeGroupCode weGroupCode); + /** + * 根据群活码id查询群活码列表 + * @param ids id列表 + * @return 结果 + */ + public List selectWeGroupCodeListByIds(List ids); + /** * 新增客户群活码 * @@ -60,4 +67,11 @@ public interface WeGroupCodeMapper extends BaseMapper * @return 结果 */ public int deleteWeGroupCodeByIds(Long[] ids); + + /** + * 检测活码名称是否唯一 + * @param activityName 活码名称 + * @return 结果 + */ + public int checkActivityNameUnique(String activityName); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeActualService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeActualService.java index c98012cd3..ff60eeb1f 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeActualService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeActualService.java @@ -2,6 +2,9 @@ package com.linkwechat.wecom.service; import com.baomidou.mybatisplus.extension.service.IService; import com.linkwechat.wecom.domain.WeGroupCodeActual; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; /** * 实际群码Service接口 @@ -11,46 +14,53 @@ import com.linkwechat.wecom.domain.WeGroupCodeActual; */ public interface IWeGroupCodeActualService extends IService { -// /** -// * 查询实际群码 -// * -// * @param id 实际群码ID -// * @return 实际群码 -// */ -// public WeGroupCodeActual selectWeGroupCodeActualById(Long id); -// -// /** -// * 查询实际群码列表 -// * -// * @param weGroupCodeActual 实际群码 -// * @return 实际群码集合 -// */ -// public List selectWeGroupCodeActualList(WeGroupCodeActual weGroupCodeActual); + /** + * 查询实际群码 + * + * @param id 实际群码ID + * @return 实际群码 + */ + public WeGroupCodeActual selectWeGroupCodeActualById(Long id); -// /** -// * 新增实际群码 -// * -// * @param weGroupCodeActual 实际群码 -// * @return 结果 -// */ -// public int insertWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual); -// -// /** -// * 修改实际群码 -// * -// * @param weGroupCodeActual 实际群码 -// * @return 结果 -// */ -// public int updateWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual); + /** + * 根据群聊id获取对应群二维码 + * @param chatId 群聊id + * @return 结果 + */ + public WeGroupCodeActual selectActualCodeByChatId(String chatId); + + /** + * 查询实际群码列表 + * + * @param weGroupCodeActual 实际群码 + * @return 实际群码集合 + */ + public List selectWeGroupCodeActualList(WeGroupCodeActual weGroupCodeActual); + + /** + * 新增实际群码 + * + * @param weGroupCodeActual 实际群码 + * @return 结果 + */ + public int insertWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual); + + /** + * 修改实际群码 + * + * @param weGroupCodeActual 实际群码 + * @return 结果 + */ + public int updateWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual); + + /** + * 批量删除实际群码 + * + * @param ids 需要删除的实际群码ID + * @return 结果 + */ + public int deleteWeGroupCodeActualByIds(Long[] ids); -// /** -// * 批量删除实际群码 -// * -// * @param ids 需要删除的实际群码ID -// * @return 结果 -// */ -// public int deleteWeGroupCodeActualByIds(Long[] ids); -// // /** // * 删除实际群码信息 // * @@ -58,4 +68,11 @@ public interface IWeGroupCodeActualService extends IService // * @return 结果 // */ // public int deleteWeGroupCodeActualById(Long id); + + /** + * 检测实际码chatId是否唯一 + * @param weGroupCodeActual 实际码 + * @return 结果 + */ + public boolean checkChatIdUnique(WeGroupCodeActual weGroupCodeActual); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeService.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeService.java index e7935c85f..061d2759b 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeService.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/IWeGroupCodeService.java @@ -4,34 +4,50 @@ import java.util.List; import com.baomidou.mybatisplus.extension.service.IService; import com.linkwechat.wecom.domain.WeGroupCode; +import com.linkwechat.wecom.domain.WeGroupCodeActual; +import org.springframework.web.multipart.MultipartFile; /** * 客户群活码Service接口 - * + * * @author ruoyi * @date 2020-10-07 */ -public interface IWeGroupCodeService extends IService -{ -// /** -// * 查询客户群活码 -// * -// * @param id 客户群活码ID -// * @return 客户群活码 -// */ -// public WeGroupCode selectWeGroupCodeById(Long id); -// -// /** -// * 查询客户群活码列表 -// * -// * @param weGroupCode 客户群活码 -// * @return 客户群活码集合 -// */ -// public List selectWeGroupCodeList(WeGroupCode weGroupCode); +public interface IWeGroupCodeService extends IService { + /** + * 查询客户群活码 + * + * @param id 客户群活码ID + * @return 客户群活码 + */ + public WeGroupCode selectWeGroupCodeById(Long id); + + /** + * 根据群活码id查询实际码列表 + * @param groupCodeId 群活码id + * @return 结果 + */ + public List selectActualListByGroupCodeId(Long groupCodeId); + + /** + * 查询客户群活码列表 + * + * @param weGroupCode 客户群活码 + * @return 客户群活码集合 + */ + public List selectWeGroupCodeList(WeGroupCode weGroupCode); + + /** + * 根据群活码id查询群活码列表 + * + * @param ids id列表 + * @return 结果 + */ + public List selectWeGroupCodeListByIds(List ids); /** * 新增客户群活码 - * + * * @param weGroupCode 客户群活码 * @return 结果 */ @@ -39,25 +55,32 @@ public interface IWeGroupCodeService extends IService /** * 修改客户群活码 - * + * * @param weGroupCode 客户群活码 * @return 结果 */ - public void updateWeGroupCode(WeGroupCode weGroupCode); - -// /** -// * 批量删除客户群活码 -// * -// * @param ids 需要删除的客户群活码ID -// * @return 结果 -// */ -// public int deleteWeGroupCodeByIds(Long[] ids); -// -// /** -// * 删除客户群活码信息 -// * -// * @param id 客户群活码ID -// * @return 结果 -// */ -// public int deleteWeGroupCodeById(Long id); + public int updateWeGroupCode(WeGroupCode weGroupCode); + + /** + * 批量删除客户群活码 + * + * @param ids 需要删除的客户群活码ID + * @return 结果 + */ + public int deleteWeGroupCodeByIds(Long[] ids); + + /** + * 删除客户群活码信息 + * + * @param id 客户群活码ID + * @return 结果 + */ + public int deleteWeGroupCodeById(Long id); + + /** + * 检测活码名称是否唯一 + * @param weGroupCode 活码对象 + * @return 结果 + */ + public boolean checkActivityNameUnique(WeGroupCode weGroupCode); } diff --git a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeGroupCodeActualServiceImpl.java b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeGroupCodeActualServiceImpl.java index 01996f032..b4254ac50 100644 --- a/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeGroupCodeActualServiceImpl.java +++ b/linkwe-wecom/src/main/java/com/linkwechat/wecom/service/impl/WeGroupCodeActualServiceImpl.java @@ -3,11 +3,17 @@ package com.linkwechat.wecom.service.impl; import java.util.List; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.linkwechat.common.config.RuoYiConfig; +import com.linkwechat.common.config.ServerConfig; +import com.linkwechat.common.exception.wecom.WeComException; +import com.linkwechat.common.utils.StringUtils; +import com.linkwechat.common.utils.file.FileUploadUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.linkwechat.wecom.mapper.WeGroupCodeActualMapper; import com.linkwechat.wecom.domain.WeGroupCodeActual; import com.linkwechat.wecom.service.IWeGroupCodeActualService; +import org.springframework.web.multipart.MultipartFile; /** * 实际群码Service业务层处理 @@ -18,70 +24,83 @@ import com.linkwechat.wecom.service.IWeGroupCodeActualService; @Service public class WeGroupCodeActualServiceImpl extends ServiceImpl implements IWeGroupCodeActualService { -// @Autowired -// private WeGroupCodeActualMapper weGroupCodeActualMapper; + @Autowired + private ServerConfig serverConfig; -// /** -// * 查询实际群码 -// * -// * @param id 实际群码ID -// * @return 实际群码 -// */ -// @Override -// public WeGroupCodeActual selectWeGroupCodeActualById(Long id) -// { -// return weGroupCodeActualMapper.selectWeGroupCodeActualById(id); -// } -// -// /** -// * 查询实际群码列表 -// * -// * @param weGroupCodeActual 实际群码 -// * @return 实际群码 -// */ -// @Override -// public List selectWeGroupCodeActualList(WeGroupCodeActual weGroupCodeActual) -// { -// return weGroupCodeActualMapper.selectWeGroupCodeActualList(weGroupCodeActual); -// } + @Autowired + private WeGroupCodeActualMapper weGroupCodeActualMapper; -// /** -// * 新增实际群码 -// * -// * @param weGroupCodeActual 实际群码 -// * @return 结果 -// */ -// @Override -// public int insertWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual) -// { -// this.sa -// return this.baseMapper.insertWeGroupCodeActual(weGroupCodeActual); -// } -// -// /** -// * 修改实际群码 -// * -// * @param weGroupCodeActual 实际群码 -// * @return 结果 -// */ -// @Override -// public int updateWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual) -// { -// return this.baseMapper.updateWeGroupCodeActual(weGroupCodeActual); -// } + /** + * 查询实际群码 + * + * @param id 实际群码ID + * @return 实际群码 + */ + @Override + public WeGroupCodeActual selectWeGroupCodeActualById(Long id) + { + return weGroupCodeActualMapper.selectWeGroupCodeActualById(id); + } + + /** + * 根据群聊id获取对应群二维码 + * + * @param chatId 群聊id + * @return 结果 + */ + @Override + public WeGroupCodeActual selectActualCodeByChatId(String chatId) { + return weGroupCodeActualMapper.selectWeGroupCodeActualByChatId(chatId); + } + + /** + * 查询实际群码列表 + * + * @param weGroupCodeActual 实际群码 + * @return 实际群码 + */ + @Override + public List selectWeGroupCodeActualList(WeGroupCodeActual weGroupCodeActual) + { + return weGroupCodeActualMapper.selectWeGroupCodeActualList(weGroupCodeActual); + } + + /** + * 新增实际群码 + * + * @param weGroupCodeActual 实际群码 + * @return 结果 + */ + @Override + public int insertWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual) + { + return weGroupCodeActualMapper.insertWeGroupCodeActual(weGroupCodeActual); + } + + /** + * 修改实际群码 + * + * @param weGroupCodeActual 实际群码 + * @return 结果 + */ + @Override + public int updateWeGroupCodeActual(WeGroupCodeActual weGroupCodeActual) + { + return this.baseMapper.updateWeGroupCodeActual(weGroupCodeActual); + } + + /** + * 批量删除实际群码 + * + * @param ids 需要删除的实际群码ID + * @return 结果 + */ + @Override + public int deleteWeGroupCodeActualByIds(Long[] ids) + { + return weGroupCodeActualMapper.deleteWeGroupCodeActualByIds(ids); + } -// /** -// * 批量删除实际群码 -// * -// * @param ids 需要删除的实际群码ID -// * @return 结果 -// */ -// @Override -// public int deleteWeGroupCodeActualByIds(Long[] ids) -// { -// return weGroupCodeActualMapper.deleteWeGroupCodeActualByIds(ids); -// } -// // /** // * 删除实际群码信息 // * @@ -93,4 +112,22 @@ public class WeGroupCodeActualServiceImpl extends ServiceImpl selectWeGroupCodeList(WeGroupCode weGroupCode) -// { -// return weGroupCodeMapper.selectWeGroupCodeList(weGroupCode); -// } + + @Autowired + private WeGroupCodeMapper weGroupCodeMapper; + + @Autowired + private WeGroupCodeActualMapper weGroupCodeActualMapper; + + @Autowired + private ServerConfig serverConfig; + + private static final String IMAGE_FORMAT = "png"; + + /** + * 查询客户群活码 + * + * @param id 客户群活码ID + * @return 客户群活码 + */ + @Override + public WeGroupCode selectWeGroupCodeById(Long id) + { + // 获取群活码 + + return weGroupCodeMapper.selectWeGroupCodeById(id); + } + + /** + * 根据群活码id查询实际码列表 + * + * @param groupCodeId 群活码id + * @return 结果 + */ + @Override + public List selectActualListByGroupCodeId(Long groupCodeId) { + return weGroupCodeActualMapper.selectActualListByGroupCodeId(groupCodeId); + } + + /** + * 查询客户群活码列表 + * + * @param weGroupCode 客户群活码 + * @return 客户群活码 + */ + @Override + public List selectWeGroupCodeList(WeGroupCode weGroupCode) + { + List weGroupCodeList = weGroupCodeMapper.selectWeGroupCodeList(weGroupCode); + for (WeGroupCode item: weGroupCodeList) { + List actualList = weGroupCodeActualMapper.selectActualListByGroupCodeId(item.getId()); + item.setActualList(actualList); + } + return weGroupCodeList; + } + + /** + * 根据群活码id查询群活码列表 + * + * @param ids id列表 + * @return 结果 + */ + @Override + public List selectWeGroupCodeListByIds(List ids) { + return weGroupCodeMapper.selectWeGroupCodeListByIds(ids); + } /** * 新增客户群活码 * * @param weGroupCode 客户群活码 - * @return 结果 */ @Override public void insertWeGroupCode(WeGroupCode weGroupCode) { + try { - if(this.save(weGroupCode)){ - List actualList = weGroupCode.getActualList(); - if(CollectionUtil.isNotEmpty(actualList)){ - iWeGroupCodeActualService.saveBatch(actualList); + String uploadDir = RuoYiConfig.getUploadPath(); + String fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + IMAGE_FORMAT; + + String filePathName = FileUploadUtils.getPathFileName(uploadDir, fileName); + + // 生成并保存二维码图片 + String logoUrl = weGroupCode.getActivityHeadUrl(); + if (StringUtils.isEmpty(logoUrl)) { + QREncode.saveQRCode(weGroupCode.getActivityName(), uploadDir + "/" + fileName); + } else { + QREncode.saveQRCode(weGroupCode.getActivityName(), logoUrl, uploadDir + "/" + fileName); } + String codeUrl = serverConfig.getUrl() + filePathName; + weGroupCode.setCodeUrl(codeUrl); + + if(this.save(weGroupCode)){ + List actualList = weGroupCode.getActualList(); + if(CollectionUtil.isNotEmpty(actualList)){ + iWeGroupCodeActualService.saveBatch(actualList); + } + } + } catch (Exception e) { + throw new WeComException(e.getMessage()); } + + } /** @@ -77,37 +144,53 @@ public class WeGroupCodeServiceImpl extends ServiceImpl actualList = weGroupCode.getActualList(); - if(CollectionUtil.isNotEmpty(actualList)){ - iWeGroupCodeActualService.updateBatchById(actualList); - } - } + int rows = weGroupCodeMapper.updateWeGroupCode(weGroupCode); + // 同时更新对应的实际码 + if (rows > 0) { + List actualList = weGroupCode.getActualList(); + if(CollectionUtil.isNotEmpty(actualList)){ + iWeGroupCodeActualService.updateBatchById(actualList); + } + } + return rows; + } + + /** + * 批量删除客户群活码 + * + * @param ids 需要删除的客户群活码ID + * @return 结果 + */ + @Override + public int deleteWeGroupCodeByIds(Long[] ids) + { + weGroupCodeActualMapper.deleteActualListByGroupCodeIds(ids); + return weGroupCodeMapper.deleteWeGroupCodeByIds(ids); + } + + /** + * 删除客户群活码信息 + * + * @param id 客户群活码ID + * @return 结果 + */ + @Override + public int deleteWeGroupCodeById(Long id) + { + return weGroupCodeMapper.deleteWeGroupCodeById(id); + } + + /** + * 检测活码名称是否唯一 + * + * @param weGroupCode 活码对象 + * @return 结果 + */ + @Override + public boolean checkActivityNameUnique(WeGroupCode weGroupCode) { + int rows = weGroupCodeMapper.checkActivityNameUnique(weGroupCode.getActivityName()); + return rows == 0; } -// -// /** -// * 批量删除客户群活码 -// * -// * @param ids 需要删除的客户群活码ID -// * @return 结果 -// */ -// @Override -// public int deleteWeGroupCodeByIds(Long[] ids) -// { -// return weGroupCodeMapper.deleteWeGroupCodeByIds(ids); -// } -// -// /** -// * 删除客户群活码信息 -// * -// * @param id 客户群活码ID -// * @return 结果 -// */ -// @Override -// public int deleteWeGroupCodeById(Long id) -// { -// return weGroupCodeMapper.deleteWeGroupCodeById(id); -// } } diff --git a/linkwe-wecom/src/main/resources/mapper/wecom/WeEmpleCodeMapper.xml b/linkwe-wecom/src/main/resources/mapper/wecom/WeEmpleCodeMapper.xml index 4dc62d03c..3afafee25 100644 --- a/linkwe-wecom/src/main/resources/mapper/wecom/WeEmpleCodeMapper.xml +++ b/linkwe-wecom/src/main/resources/mapper/wecom/WeEmpleCodeMapper.xml @@ -102,7 +102,7 @@ @@ -30,13 +32,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and group_code_id = #{groupCodeId} and scan_code_times = #{scanCodeTimes} and status = #{status} + and chat_id = #{chatId} + and chat_group_name = #{chatGroupName} - + + + + + + + + insert into we_group_code_actual @@ -47,6 +70,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" effect_time, scan_code_times_limit, group_code_id, + chat_id, + chat_group_name, scan_code_times, del_flag, status, @@ -58,6 +83,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{effectTime}, #{scanCodeTimesLimit}, #{groupCodeId}, + #{chatId}, + #{chatGroupName}, #{scanCodeTimes}, #{delFlag}, #{status}, @@ -72,6 +99,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" effect_time = #{effectTime}, scan_code_times_limit = #{scanCodeTimesLimit}, group_code_id = #{groupCodeId}, + chat_id = #{chatId}, + chat_group_name = #{chatGroupName}, scan_code_times = #{scanCodeTimes}, del_flag = #{delFlag}, status = #{status}, @@ -89,5 +118,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{id} + + + delete from we_group_code_actual where group_code_id in + + #{id} + + \ No newline at end of file diff --git a/linkwe-wecom/src/main/resources/mapper/wecom/WeGroupCodeMapper.xml b/linkwe-wecom/src/main/resources/mapper/wecom/WeGroupCodeMapper.xml index a32892dd3..f48209f6f 100644 --- a/linkwe-wecom/src/main/resources/mapper/wecom/WeGroupCodeMapper.xml +++ b/linkwe-wecom/src/main/resources/mapper/wecom/WeGroupCodeMapper.xml @@ -1,74 +1,136 @@ + PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + - select id, activity_head_url, activity_name, activity_scene, guide, join_group_is_tip, tip_msg, customer_server_qr_code, del_flag from we_group_code + SELECT + w.id, + w.code_url, + w.activity_head_url, + w.activity_name, + w.activity_desc, + w.activity_scene, + w.guide, + w.join_group_is_tip, + w.tip_msg, + w.customer_server_qr_code, + w.del_flag, + w.create_by, + w.create_time, + w.update_by, + w.update_time, + w.remark, + IFNULL((SELECT count(1) FROM we_group_code_actual WHERE (SELECT IFNULL(scan_code_times, 0)) < scan_code_times_limit AND group_code_id = w.id ), 0) AS available_codes, + IFNULL((SELECT SUM( scan_code_times ) AS scan_times FROM we_group_code_actual GROUP BY group_code_id HAVING group_code_id = w.id), 0) AS total_scan_times, + IFNULL((SELECT count(1) FROM we_group_code_actual WHERE group_code_id = w.id AND TIMESTAMPDIFF(DAY, sysdate(), effect_time ) < 3), 0) AS about_to_expire_codes + FROM + we_group_code w + - + + + - + + + insert into we_group_code id, + code_url, activity_head_url, activity_name, + activity_desc, activity_scene, guide, join_group_is_tip, tip_msg, customer_server_qr_code, del_flag, - + #{id}, + #{codeUrl}, #{activityHeadUrl}, #{activityName}, + #{activityDesc}, #{activityScene}, #{guide}, #{joinGroupIsTip}, #{tipMsg}, #{customerServerQrCode}, #{delFlag}, - + update we_group_code - activity_head_url = #{activityHeadUrl}, activity_name = #{activityName}, + activity_head_url = #{activityHeadUrl}, + activity_desc = #{activityDesc}, activity_scene = #{activityScene}, guide = #{guide}, join_group_is_tip = #{joinGroupIsTip}, @@ -84,10 +146,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - delete from we_group_code where id in + delete from we_group_code where id in #{id} - + \ No newline at end of file diff --git a/sql/link-wechat.sql b/sql/link-wechat.sql index 7fee43a30..75d867f20 100644 --- a/sql/link-wechat.sql +++ b/sql/link-wechat.sql @@ -6010,19 +6010,25 @@ CREATE TABLE `we_group` ( -- ---------------------------- -- Table structure for we_group_code -- ---------------------------- -DROP TABLE IF EXISTS `we_group_code`; CREATE TABLE `we_group_code` ( `id` bigint(20) NOT NULL, - `activity_head_url` varchar(60) DEFAULT NULL COMMENT '活动头像', - `activity_name` varchar(60) DEFAULT NULL COMMENT '活动名称', + `code_url` varchar(255) NOT NULL COMMENT '二维码链接', + `activity_head_url` varchar(255) DEFAULT NULL COMMENT '活码头像链接', + `activity_name` varchar(60) DEFAULT NULL COMMENT '活码名称', + `activity_desc` varchar(60) DEFAULT NULL COMMENT '活码描述', `activity_scene` varchar(60) DEFAULT NULL COMMENT '场景', `guide` varchar(60) DEFAULT NULL COMMENT '引导语', `join_group_is_tip` tinyint(4) DEFAULT NULL COMMENT '进群是否提示:1:是;0:否;', `tip_msg` varchar(100) DEFAULT NULL COMMENT '进群提示语', `customer_server_qr_code` varchar(100) DEFAULT NULL COMMENT '客服二维码', `del_flag` tinyint(4) DEFAULT NULL COMMENT '0:正常;2:删除;', + `create_by` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='客户群活码'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='客户群活码' -- ---------------------------- -- Records of we_group_code @@ -6039,7 +6045,9 @@ CREATE TABLE `we_group_code_actual` ( `effect_time` datetime DEFAULT NULL COMMENT '有效期', `scan_code_times_limit` int(11) DEFAULT NULL COMMENT '扫码次数限制', `group_code_id` bigint(20) DEFAULT NULL COMMENT '群活码id', - `scan_code_times` int(11) DEFAULT NULL COMMENT '扫码次数', + `chat_id` bigint(20) DEFAULT NULL COMMENT '群聊id', + `chat_group_name` bigint(20) DEFAULT NULL COMMENT '群聊名称', + `scan_code_times` int(11) DEFAULT 0 COMMENT '扫码次数', `del_flag` tinyint(4) DEFAULT NULL COMMENT '0:正常使用;2:删除;', `status` tinyint(4) DEFAULT '0' COMMENT '0:使用中', PRIMARY KEY (`id`) -- Gitee From 183be53b1b352549ba898a243ac9bd9bf8f581ca Mon Sep 17 00:00:00 2001 From: unknown <1104423600@qq.com> Date: Mon, 25 Jan 2021 11:21:44 +0800 Subject: [PATCH 056/177] =?UTF-8?q?=E7=BE=A4=E6=B4=BB=E7=A0=81=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linkwe-ui/src/api/drainageCode/actual.js | 72 ++ linkwe-ui/src/api/drainageCode/group.js | 116 ++++ linkwe-ui/src/assets/example/groupCode.png | Bin 0 -> 69703 bytes .../src/views/drainageCode/group/add.vue | 509 +++----------- .../src/views/drainageCode/group/baseInfo.vue | 357 ++++++++++ .../src/views/drainageCode/group/customer.vue | 181 +++++ .../src/views/drainageCode/group/detail.vue | 343 +--------- .../views/drainageCode/group/groupCode.vue | 132 ++++ .../src/views/drainageCode/group/list.vue | 637 +++++++++++------ .../src/views/drainageCode/group/realCode.vue | 640 ++++++++++++++++-- 10 files changed, 1979 insertions(+), 1008 deletions(-) create mode 100644 linkwe-ui/src/api/drainageCode/actual.js create mode 100644 linkwe-ui/src/api/drainageCode/group.js create mode 100644 linkwe-ui/src/assets/example/groupCode.png create mode 100644 linkwe-ui/src/views/drainageCode/group/customer.vue create mode 100644 linkwe-ui/src/views/drainageCode/group/groupCode.vue diff --git a/linkwe-ui/src/api/drainageCode/actual.js b/linkwe-ui/src/api/drainageCode/actual.js new file mode 100644 index 000000000..36e585d3a --- /dev/null +++ b/linkwe-ui/src/api/drainageCode/actual.js @@ -0,0 +1,72 @@ +import request from '@/utils/request' +const service = window.CONFIG.services.wecom + '/actual' + +/** + * 获取实际群码 + * @param {*} params + { + "groupCodeId": "群活码ID", + "pageNum": "当前页", + "pageSize": "每页显示条数", + "state": "状态", + } + */ +export function getList(params) { + return request({ + url: service + '/list', + params + }) +} + +/** + * 添加实际群码 + * @param {*} data +{ + "groupName": 群名称, + "actualGroupQrCode": 实际群码图片, + "effectTime": 有效期, + "scanCodeTimesLimit": 扫码次数限制, + "chatId": 客户群, + "chatGroupName": 客户群名称 +} + */ +export function add(data) { + return request({ + url: service + '/', + method: 'post', + data + }) +} + +/** + * 添加实际群码 + * @param {*} data +{ + "id": 实际群码ID, + "groupName": 群名称, + "actualGroupQrCode": 实际群码图片, + "effectTime": 有效期, + "scanCodeTimesLimit": 扫码次数限制, + "chatId": 客户群, + "chatGroupName": 客户群名称 +} + */ +export function update(data) { + return request({ + url: service + '/', + method: 'put', + data + }) +} + +/** + * 删除实际群码 + * @param {*} + * "ids": 实际群活ID,多个ID以逗号分隔 + */ +export function remove(ids) { + return request({ + url: service + '/' + ids, + method: 'delete', + }) +} diff --git a/linkwe-ui/src/api/drainageCode/group.js b/linkwe-ui/src/api/drainageCode/group.js new file mode 100644 index 000000000..0ca5eb857 --- /dev/null +++ b/linkwe-ui/src/api/drainageCode/group.js @@ -0,0 +1,116 @@ +import request from '@/utils/request' +const service = window.CONFIG.services.wecom + '/groupCode' + +/** + * 获取群活码列表 + * @param {*} params +{ + "pageNum": "当前页", + "pageSize": "每页显示条数", + "activityName": "活码名", + "createBy": "创建人", + "beginTime": "开始时间", + "endTime": "结束时间" +} + */ +export function getList(params) { + return request({ + url: service + '/list', + params + }) +} + +/** + * 新增群活码 + * @param {*} data +{ + "file": 活码头像 + "activityName": 活码名称 + "activityDesc": 活码描述 + "guide": 引导语 + "joinGroupIsTip": 进群是否提示:1:是;0:否 + "tipMsg": 进群提示语 + "customerServerQrCode":客服二维码 +} + */ +export function add(data) { + return request({ + url: service + '/', + method: 'post', + data, + }) +} + +/** + * 更新群活码 + * @param {*} id 群活码ID + * @param {*} data +{ + "file": 活码头像 + "activityName": 活码名称 + "activityDesc": 活码描述 + "guide": 引导语 + "joinGroupIsTip": 进群是否提示:1:是;0:否 + "tipMsg": 进群提示语 + "customerServerQrCode":客服二维码 +} + */ +export function update(id, data) { + return request({ + url: service + '/' + id, + method: 'put', + data, + }) +} + +/** + * 根据ID获取群活码数据 + * @param {*} params + */ +export function getDetail(id) { + return request({ + url: service + '/' + id, + }) +} + +/** + * 删除群活码 + * @param {*} + * "ids": 群活码ID,多个ID以逗号分隔 + */ +export function remove(ids) { + return request({ + url: service + '/' + ids, + method: 'delete', + }) +} + +/** + * 批量下载群活码 + * @param {*} + * "ids": 群活码ID,多个ID以逗号分隔 + */ +export function downloadBatch(ids) { + return request({ + url: service + '/downloadBatch', + params: { + ids, + }, + responseType: 'blob' + }) +} + +/** + * 下载群活码 + * @param {*} + * "id": 群活码ID,多个ID以逗号分隔 + */ +export function download(id) { + return request({ + url: service + '/download', + params: { + id, + }, + responseType: 'blob' + }) +} diff --git a/linkwe-ui/src/assets/example/groupCode.png b/linkwe-ui/src/assets/example/groupCode.png new file mode 100644 index 0000000000000000000000000000000000000000..a035dd6e5bc64da7a2d31260948e27b9eea01f1b GIT binary patch literal 69703 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 XdL`_XM)`8f$*w{NuCG}IwF&<(+P15A literal 0 HcmV?d00001 diff --git a/linkwe-ui/src/views/drainageCode/group/add.vue b/linkwe-ui/src/views/drainageCode/group/add.vue index e96982138..25a8826ef 100644 --- a/linkwe-ui/src/views/drainageCode/group/add.vue +++ b/linkwe-ui/src/views/drainageCode/group/add.vue @@ -1,449 +1,104 @@ - \ No newline at end of file + + // 管理实际群活码 + handleRealCode() { + this.next() + } + } +} + + + diff --git a/linkwe-ui/src/views/drainageCode/group/baseInfo.vue b/linkwe-ui/src/views/drainageCode/group/baseInfo.vue index e69de29bb..76ce911d8 100644 --- a/linkwe-ui/src/views/drainageCode/group/baseInfo.vue +++ b/linkwe-ui/src/views/drainageCode/group/baseInfo.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/linkwe-ui/src/views/drainageCode/group/customer.vue b/linkwe-ui/src/views/drainageCode/group/customer.vue new file mode 100644 index 000000000..7204a299d --- /dev/null +++ b/linkwe-ui/src/views/drainageCode/group/customer.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/linkwe-ui/src/views/drainageCode/group/detail.vue b/linkwe-ui/src/views/drainageCode/group/detail.vue index 38239e732..407d2ab10 100644 --- a/linkwe-ui/src/views/drainageCode/group/detail.vue +++ b/linkwe-ui/src/views/drainageCode/group/detail.vue @@ -1,325 +1,42 @@ - \ No newline at end of file + + created () { + this.groupCodeId = this.$route.query.groupCodeId + } +} + + + diff --git a/linkwe-ui/src/views/drainageCode/group/groupCode.vue b/linkwe-ui/src/views/drainageCode/group/groupCode.vue new file mode 100644 index 000000000..6485494f7 --- /dev/null +++ b/linkwe-ui/src/views/drainageCode/group/groupCode.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/linkwe-ui/src/views/drainageCode/group/list.vue b/linkwe-ui/src/views/drainageCode/group/list.vue index c22cd0f61..d2b8ecb7c 100644 --- a/linkwe-ui/src/views/drainageCode/group/list.vue +++ b/linkwe-ui/src/views/drainageCode/group/list.vue @@ -1,251 +1,486 @@ -