Commit d1cbe0c3fd4e18766a46f59f7a0eb1dbd99e28cd
1 parent
90ffe5ad
mp:增加图文草稿箱的删除、发布功能
Showing
5 changed files
with
98 additions
and
46 deletions
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 | } |