Commit d1cbe0c3fd4e18766a46f59f7a0eb1dbd99e28cd

Authored by YunaiV
1 parent 90ffe5ad

mp:增加图文草稿箱的删除、发布功能

yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java
@@ -47,6 +47,7 @@ public interface ErrorCodeConstants { @@ -47,6 +47,7 @@ public interface ErrorCodeConstants {
47 ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}"); 47 ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}");
48 ErrorCode DRAFT_CREATE_FAIL = new ErrorCode(1006007001, "创建草稿失败,原因:{}"); 48 ErrorCode DRAFT_CREATE_FAIL = new ErrorCode(1006007001, "创建草稿失败,原因:{}");
49 ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}"); 49 ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}");
  50 + ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007002, "删除草稿失败,原因:{}");
50 51
51 // TODO 要处理下 52 // TODO 要处理下
52 ErrorCode MENU_NOT_EXISTS = new ErrorCode(1006001002, "菜单不存在"); 53 ErrorCode MENU_NOT_EXISTS = new ErrorCode(1006001002, "菜单不存在");
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpDraftController.java
1 package cn.iocoder.yudao.module.mp.controller.admin.news; 1 package cn.iocoder.yudao.module.mp.controller.admin.news;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 7 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
6 import cn.iocoder.yudao.module.mp.controller.admin.news.vo.MpDraftPageReqVO; 8 import cn.iocoder.yudao.module.mp.controller.admin.news.vo.MpDraftPageReqVO;
  9 +import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO;
7 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; 10 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
  11 +import cn.iocoder.yudao.module.mp.service.material.MpMaterialService;
8 import io.swagger.annotations.Api; 12 import io.swagger.annotations.Api;
9 import io.swagger.annotations.ApiImplicitParam; 13 import io.swagger.annotations.ApiImplicitParam;
10 import io.swagger.annotations.ApiImplicitParams; 14 import io.swagger.annotations.ApiImplicitParams;
@@ -12,17 +16,21 @@ import io.swagger.annotations.ApiOperation; @@ -12,17 +16,21 @@ import io.swagger.annotations.ApiOperation;
12 import me.chanjar.weixin.common.error.WxErrorException; 16 import me.chanjar.weixin.common.error.WxErrorException;
13 import me.chanjar.weixin.mp.api.WxMpService; 17 import me.chanjar.weixin.mp.api.WxMpService;
14 import me.chanjar.weixin.mp.bean.draft.*; 18 import me.chanjar.weixin.mp.bean.draft.*;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
15 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
16 import org.springframework.web.bind.annotation.*; 21 import org.springframework.web.bind.annotation.*;
17 22
18 import javax.annotation.Resource; 23 import javax.annotation.Resource;
  24 +import java.util.HashSet;
19 import java.util.List; 25 import java.util.List;
  26 +import java.util.Map;
  27 +import java.util.Set;
20 28
21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
23 import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*; 32 import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
24 33
25 -// TODO 芋艿:权限  
26 @Api(tags = "管理后台 - 公众号草稿") 34 @Api(tags = "管理后台 - 公众号草稿")
27 @RestController 35 @RestController
28 @RequestMapping("/mp/draft") 36 @RequestMapping("/mp/draft")
@@ -32,8 +40,12 @@ public class MpDraftController { @@ -32,8 +40,12 @@ public class MpDraftController {
32 @Resource 40 @Resource
33 private MpServiceFactory mpServiceFactory; 41 private MpServiceFactory mpServiceFactory;
34 42
  43 + @Resource
  44 + private MpMaterialService mpMaterialService;
  45 +
35 @GetMapping("/page") 46 @GetMapping("/page")
36 @ApiOperation("获得草稿分页") 47 @ApiOperation("获得草稿分页")
  48 + @PreAuthorize("@ss.hasPermission('mp:draft:query')")
37 public CommonResult<PageResult<WxMpDraftItem>> getDraftPage(MpDraftPageReqVO reqVO) { 49 public CommonResult<PageResult<WxMpDraftItem>> getDraftPage(MpDraftPageReqVO reqVO) {
38 // 从公众号查询草稿箱 50 // 从公众号查询草稿箱
39 WxMpService mpService = mpServiceFactory.getRequiredMpService(reqVO.getAccountId()); 51 WxMpService mpService = mpServiceFactory.getRequiredMpService(reqVO.getAccountId());
@@ -43,16 +55,35 @@ public class MpDraftController { @@ -43,16 +55,35 @@ public class MpDraftController {
43 } catch (WxErrorException e) { 55 } catch (WxErrorException e) {
44 throw exception(DRAFT_LIST_FAIL, e.getError().getErrorMsg()); 56 throw exception(DRAFT_LIST_FAIL, e.getError().getErrorMsg());
45 } 57 }
  58 + // 查询对应的图片地址。目的:解决公众号的图片链接无法在我们后台展示
  59 + setDraftThumbUrl(draftList.getItems());
46 60
47 // 返回分页 61 // 返回分页
48 return success(new PageResult<>(draftList.getItems(), draftList.getTotalCount().longValue())); 62 return success(new PageResult<>(draftList.getItems(), draftList.getTotalCount().longValue()));
49 } 63 }
50 64
  65 + private void setDraftThumbUrl(List<WxMpDraftItem> items) {
  66 + // 1.1 获得 mediaId 数组
  67 + Set<String> mediaIds = new HashSet<>();
  68 + items.forEach(item -> item.getContent().getNewsItem().forEach(newsItem -> mediaIds.add(newsItem.getThumbMediaId())));
  69 + if (CollUtil.isEmpty(mediaIds)) {
  70 + return;
  71 + }
  72 + // 1.2 批量查询对应的 Media 素材
  73 + Map<String, MpMaterialDO> materials = CollectionUtils.convertMap(mpMaterialService.getMaterialListByMediaId(mediaIds),
  74 + MpMaterialDO::getMediaId);
  75 +
  76 + // 2. 设置回 WxMpDraftItem 记录
  77 + items.forEach(item -> item.getContent().getNewsItem().forEach(newsItem ->
  78 + findAndThen(materials, newsItem.getThumbMediaId(), material -> newsItem.setThumbUrl(material.getUrl()))));
  79 + }
  80 +
51 @PostMapping("/create") 81 @PostMapping("/create")
52 @ApiOperation("创建草稿") 82 @ApiOperation("创建草稿")
53 @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true, 83 @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true,
54 example = "1024", dataTypeClass = Long.class) 84 example = "1024", dataTypeClass = Long.class)
55 - public CommonResult<String> createDraft(@RequestParam("accountId") Long accountId, 85 + @PreAuthorize("@ss.hasPermission('mp:draft:create')")
  86 + public CommonResult<String> deleteDraft(@RequestParam("accountId") Long accountId,
56 @RequestBody WxMpAddDraft draft) { 87 @RequestBody WxMpAddDraft draft) {
57 WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId); 88 WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
58 try { 89 try {
@@ -71,7 +102,8 @@ public class MpDraftController { @@ -71,7 +102,8 @@ public class MpDraftController {
71 @ApiImplicitParam(name = "mediaId", value = "草稿素材的编号", required = true, 102 @ApiImplicitParam(name = "mediaId", value = "草稿素材的编号", required = true,
72 example = "xxx", dataTypeClass = String.class), 103 example = "xxx", dataTypeClass = String.class),
73 }) 104 })
74 - public CommonResult<Boolean> createDraft(@RequestParam("accountId") Long accountId, 105 + @PreAuthorize("@ss.hasPermission('mp:draft:update')")
  106 + public CommonResult<Boolean> deleteDraft(@RequestParam("accountId") Long accountId,
75 @RequestParam("mediaId") String mediaId, 107 @RequestParam("mediaId") String mediaId,
76 @RequestBody List<WxMpDraftArticles> articles) { 108 @RequestBody List<WxMpDraftArticles> articles) {
77 WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId); 109 WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
@@ -86,4 +118,24 @@ public class MpDraftController { @@ -86,4 +118,24 @@ public class MpDraftController {
86 } 118 }
87 } 119 }
88 120
  121 + @DeleteMapping("/delete")
  122 + @ApiOperation("删除草稿")
  123 + @ApiImplicitParams({
  124 + @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true,
  125 + example = "1024", dataTypeClass = Long.class),
  126 + @ApiImplicitParam(name = "mediaId", value = "草稿素材的编号", required = true,
  127 + example = "xxx", dataTypeClass = String.class),
  128 + })
  129 + @PreAuthorize("@ss.hasPermission('mp:draft:delete')")
  130 + public CommonResult<Boolean> deleteDraft(@RequestParam("accountId") Long accountId,
  131 + @RequestParam("mediaId") String mediaId) {
  132 + WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
  133 + try {
  134 + mpService.getDraftService().delDraft(mediaId);
  135 + return success(true);
  136 + } catch (WxErrorException e) {
  137 + throw exception(DRAFT_DELETE_FAIL, e.getError().getErrorMsg());
  138 + }
  139 + }
  140 +
89 } 141 }
yudao-ui-admin/src/api/mp/draft.js
@@ -28,3 +28,11 @@ export function updateDraft(accountId, mediaId, articles) { @@ -28,3 +28,11 @@ export function updateDraft(accountId, mediaId, articles) {
28 data: articles 28 data: articles
29 }) 29 })
30 } 30 }
  31 +
  32 +// 删除草稿
  33 +export function deleteDraft(accountId, mediaId) {
  34 + return request({
  35 + url: '/mp/draft/delete?accountId=' + accountId + '&mediaId=' + mediaId,
  36 + method: 'delete',
  37 + })
  38 +}
yudao-ui-admin/src/api/mp/freePublish.js
@@ -16,3 +16,11 @@ export function deleteFreePublish(accountId, articleId) { @@ -16,3 +16,11 @@ export function deleteFreePublish(accountId, articleId) {
16 method: 'delete' 16 method: 'delete'
17 }) 17 })
18 } 18 }
  19 +
  20 +// 发布公众号素材
  21 +export function submitFreePublish(accountId, mediaId) {
  22 + return request({
  23 + url: '/mp/free-publish/submit?accountId=' + accountId + '&mediaId=' + mediaId,
  24 + method: 'post'
  25 + })
  26 +}
yudao-ui-admin/src/views/mp/draft/index.vue
@@ -22,6 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE @@ -22,6 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE. 22 SOFTWARE.
23 芋道源码: 23 芋道源码:
24 ① 优化代码,和项目的代码保持一致 24 ① 优化代码,和项目的代码保持一致
  25 + ② 清理冗余代码,保证代码整洁
  26 + ③ 增加注释,提升可读性
25 --> 27 -->
26 <template> 28 <template>
27 <div class="app-container"> 29 <div class="app-container">
@@ -53,11 +55,11 @@ SOFTWARE. @@ -53,11 +55,11 @@ SOFTWARE.
53 <div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list" 55 <div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list"
54 :key='item.articleId'> 56 :key='item.articleId'>
55 <wx-news :articles="item.content.newsItem" /> 57 <wx-news :articles="item.content.newsItem" />
56 - <!-- TODO 芋艿:权限、样式(搜索框之类的) --> 58 + <!-- 操作按钮 -->
57 <el-row class="ope-row"> 59 <el-row class="ope-row">
58 - <el-button type="success" circle @click="handlePublishNews(item)">发布</el-button>  
59 - <el-button type="primary" icon="el-icon-edit" circle @click="handleUpdate(item)"></el-button>  
60 - <el-button type="danger" icon="el-icon-delete" circle @click="delMaterial(item)"></el-button> 60 + <el-button type="success" circle @click="handlePublish(item)" v-hasPermi="['mp:free-publish:submit']">发布</el-button>
  61 + <el-button type="primary" icon="el-icon-edit" circle @click="handleUpdate(item)" v-hasPermi="['mp:draft:update']" />
  62 + <el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)" v-hasPermi="['mp:draft:delete']" />
61 </el-row> 63 </el-row>
62 </div> 64 </div>
63 </div> 65 </div>
@@ -147,7 +149,6 @@ SOFTWARE. @@ -147,7 +149,6 @@ SOFTWARE.
147 <wx-editor v-model="articlesAdd[isActiveAddNews].content" :account-id="this.uploadData.accountId" 149 <wx-editor v-model="articlesAdd[isActiveAddNews].content" :account-id="this.uploadData.accountId"
148 v-if="hackResetEditor"/> 150 v-if="hackResetEditor"/>
149 </el-row> 151 </el-row>
150 - <!-- 原文地址 -->  
151 </div> 152 </div>
152 <div slot="footer" class="dialog-footer"> 153 <div slot="footer" class="dialog-footer">
153 <el-button @click="dialogNewsVisible = false">取 消</el-button> 154 <el-button @click="dialogNewsVisible = false">取 消</el-button>
@@ -158,13 +159,13 @@ SOFTWARE. @@ -158,13 +159,13 @@ SOFTWARE.
158 </template> 159 </template>
159 160
160 <script> 161 <script>
161 -// import { getPage as getPage1 } from '@/api/wxmp/wxmaterial'  
162 import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue'; 162 import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue';
163 import WxNews from '@/views/mp/components/wx-news/main.vue'; 163 import WxNews from '@/views/mp/components/wx-news/main.vue';
164 import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' 164 import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
165 import { getAccessToken } from '@/utils/auth' 165 import { getAccessToken } from '@/utils/auth'
166 -import {createDraft, getDraftPage, updateDraft} from "@/api/mp/draft"; 166 +import {createDraft, deleteDraft, getDraftPage, updateDraft} from "@/api/mp/draft";
167 import { getSimpleAccounts } from "@/api/mp/account"; 167 import { getSimpleAccounts } from "@/api/mp/account";
  168 +import {deleteFreePublish, submitFreePublish} from "@/api/mp/freePublish";
168 169
169 export default { 170 export default {
170 name: 'mpDraft', 171 name: 'mpDraft',
@@ -221,7 +222,7 @@ export default { @@ -221,7 +222,7 @@ export default {
221 this.setAccountId(this.accounts[0].id); 222 this.setAccountId(this.accounts[0].id);
222 } 223 }
223 // 加载数据 224 // 加载数据
224 - // this.getList(); // TODO 芋艿:开发完,放出来 225 + this.getList();
225 }) 226 })
226 }, 227 },
227 methods: { 228 methods: {
@@ -434,44 +435,26 @@ export default { @@ -434,44 +435,26 @@ export default {
434 }, 435 },
435 436
436 // ======================== 草稿箱发布 ======================== 437 // ======================== 草稿箱发布 ========================
437 - handlePublishNews(item){  
438 - this.$confirm('你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。', '提示', {  
439 - confirmButtonText: '确定',  
440 - cancelButtonText: '取消',  
441 - type: 'warning' 438 + handlePublish(item) {
  439 + const accountId = this.queryParams.accountId;
  440 + const mediaId = item.mediaId;
  441 + const content = '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。';
  442 + this.$modal.confirm(content).then(function() {
  443 + return submitFreePublish(accountId, mediaId);
442 }).then(() => { 444 }).then(() => {
443 - this.loading = true  
444 - publish(item.mediaId).then(response => {  
445 - this.loading = false  
446 - this.$message.success('发布任务提交成功')  
447 - this.getList(this.queryParams)  
448 - }).catch(() => {  
449 - this.loading = false  
450 - })  
451 - }).catch(() => {  
452 - }) 445 + this.getList();
  446 + this.$modal.msgSuccess("发布成功");
  447 + }).catch(() => {});
453 }, 448 },
454 - delMaterial(item){  
455 - this.$confirm('此操作将永久删除该草稿, 是否继续?', '提示', {  
456 - confirmButtonText: '确定',  
457 - cancelButtonText: '取消',  
458 - type: 'warning' 449 + handleDelete(item) {
  450 + const accountId = this.queryParams.accountId;
  451 + const mediaId = item.mediaId;
  452 + this.$modal.confirm('此操作将永久删除该草稿, 是否继续?').then(function() {
  453 + return deleteDraft(accountId, mediaId);
459 }).then(() => { 454 }).then(() => {
460 - this.loading = true  
461 - delObj({  
462 - id:item.mediaId  
463 - }).then(response => {  
464 - this.loading = false  
465 - if(response.code == 200){  
466 - this.getList(this.queryParams)  
467 - }else{  
468 - this.loading = false  
469 - this.$message.error('删除出错:' + response.msg)  
470 - }  
471 - }).catch(() => {  
472 - this.loading = false  
473 - })  
474 - }) 455 + this.getList();
  456 + this.$modal.msgSuccess("删除成功");
  457 + }).catch(() => {});
475 }, 458 },
476 } 459 }
477 } 460 }