mp:完善【发布功能】的相关代码

This commit is contained in:
YunaiV 2023-01-13 07:33:25 +08:00
parent 9f7b646f6c
commit 1e63198f4a
8 changed files with 483 additions and 2 deletions

View File

@ -40,6 +40,7 @@ public interface ErrorCodeConstants {
// ========== 公众号发布能力 1006006000============ // ========== 公众号发布能力 1006006000============
ErrorCode FREE_PUBLISH_LIST_FAIL = new ErrorCode(1006006000, "获得已成功发布列表失败,原因:{}"); ErrorCode FREE_PUBLISH_LIST_FAIL = new ErrorCode(1006006000, "获得已成功发布列表失败,原因:{}");
ErrorCode FREE_PUBLISH_SUBMIT_FAIL = new ErrorCode(1006006001, "提交发布失败,原因:{}"); ErrorCode FREE_PUBLISH_SUBMIT_FAIL = new ErrorCode(1006006001, "提交发布失败,原因:{}");
ErrorCode FREE_PUBLISH_DELETE_FAIL = new ErrorCode(1006006001, "删除发布失败,原因:{}");
// ========== 公众号草稿 1006007000============ // ========== 公众号草稿 1006007000============
ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}"); ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}");

View File

@ -1,10 +1,14 @@
package cn.iocoder.yudao.module.mp.controller.admin.news; package cn.iocoder.yudao.module.mp.controller.admin.news;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.module.mp.controller.admin.news.vo.MpFreePublishPageReqVO; import cn.iocoder.yudao.module.mp.controller.admin.news.vo.MpFreePublishPageReqVO;
import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO;
import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
import cn.iocoder.yudao.module.mp.service.material.MpMaterialService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiImplicitParams;
@ -13,16 +17,21 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishItem; import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishItem;
import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishList; import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishList;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
// TODO 芋艿权限
@Api(tags = "管理后台 - 公众号发布能力") @Api(tags = "管理后台 - 公众号发布能力")
@RestController @RestController
@RequestMapping("/mp/free-publish") @RequestMapping("/mp/free-publish")
@ -32,8 +41,12 @@ public class MpFreePublishController {
@Resource @Resource
private MpServiceFactory mpServiceFactory; private MpServiceFactory mpServiceFactory;
@Resource
private MpMaterialService mpMaterialService;
@GetMapping("/page") @GetMapping("/page")
@ApiOperation("获得已发布的图文分页") @ApiOperation("获得已发布的图文分页")
@PreAuthorize("@ss.hasPermission('mp:free-publish:query')")
public CommonResult<PageResult<WxMpFreePublishItem>> getFreePublishPage(MpFreePublishPageReqVO reqVO) { public CommonResult<PageResult<WxMpFreePublishItem>> getFreePublishPage(MpFreePublishPageReqVO reqVO) {
// 从公众号查询已发布的图文列表 // 从公众号查询已发布的图文列表
WxMpService mpService = mpServiceFactory.getRequiredMpService(reqVO.getAccountId()); WxMpService mpService = mpServiceFactory.getRequiredMpService(reqVO.getAccountId());
@ -44,12 +57,29 @@ public class MpFreePublishController {
} catch (WxErrorException e) { } catch (WxErrorException e) {
throw exception(FREE_PUBLISH_LIST_FAIL, e.getError().getErrorMsg()); throw exception(FREE_PUBLISH_LIST_FAIL, e.getError().getErrorMsg());
} }
// todo 芋艿需要查询对应的缩略图不然前端无法展示 // 查询对应的图片地址目的解决公众号的图片链接无法在我们后台展示
setFreePublishThumbUrl(publicationRecords.getItems());
// 返回分页 // 返回分页
return success(new PageResult<>(publicationRecords.getItems(), publicationRecords.getTotalCount().longValue())); return success(new PageResult<>(publicationRecords.getItems(), publicationRecords.getTotalCount().longValue()));
} }
private void setFreePublishThumbUrl(List<WxMpFreePublishItem> items) {
// 1.1 获得 mediaId 数组
Set<String> mediaIds = new HashSet<>();
items.forEach(item -> item.getContent().getNewsItem().forEach(newsItem -> mediaIds.add(newsItem.getThumbMediaId())));
if (CollUtil.isEmpty(mediaIds)) {
return;
}
// 1.2 批量查询对应的 Media 素材
Map<String, MpMaterialDO> materials = CollectionUtils.convertMap(mpMaterialService.getMaterialListByMediaId(mediaIds),
MpMaterialDO::getMediaId);
// 2. 设置回 WxMpFreePublishItem 记录
items.forEach(item -> item.getContent().getNewsItem().forEach(newsItem ->
findAndThen(materials, newsItem.getThumbMediaId(), material -> newsItem.setThumbUrl(material.getUrl()))));
}
@PostMapping("/submit") @PostMapping("/submit")
@ApiOperation("发布草稿") @ApiOperation("发布草稿")
@ApiImplicitParams({ @ApiImplicitParams({
@ -58,6 +88,7 @@ public class MpFreePublishController {
@ApiImplicitParam(name = "mediaId", value = "要发布的草稿的 media_id", required = true, @ApiImplicitParam(name = "mediaId", value = "要发布的草稿的 media_id", required = true,
example = "2048", dataTypeClass = String.class) example = "2048", dataTypeClass = String.class)
}) })
@PreAuthorize("@ss.hasPermission('mp:free-publish:submit')")
public CommonResult<String> submitFreePublish(@RequestParam("accountId") Long accountId, public CommonResult<String> submitFreePublish(@RequestParam("accountId") Long accountId,
@RequestParam("mediaId") String mediaId) { @RequestParam("mediaId") String mediaId) {
WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId); WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
@ -69,5 +100,24 @@ public class MpFreePublishController {
} }
} }
@DeleteMapping("/delete")
@ApiOperation("删除草稿")
@ApiImplicitParams({
@ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true,
example = "1024", dataTypeClass = Long.class),
@ApiImplicitParam(name = "articleId", value = "发布记录的编号", required = true,
example = "2048", dataTypeClass = String.class)
})
@PreAuthorize("@ss.hasPermission('mp:free-publish:delete')")
public CommonResult<Boolean> deleteFreePublish(@RequestParam("accountId") Long accountId,
@RequestParam("articleId") String articleId) {
WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
try {
mpService.getFreePublishService().deletePushAllArticle(articleId);
return success(true);
} catch (WxErrorException e) {
throw exception(FREE_PUBLISH_DELETE_FAIL, e.getError().getErrorMsg());
}
}
} }

View File

@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.mp.controller.admin.material.vo.MpMaterialPageReq
import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO; import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper @Mapper
public interface MpMaterialMapper extends BaseMapperX<MpMaterialDO> { public interface MpMaterialMapper extends BaseMapperX<MpMaterialDO> {
@ -21,4 +24,8 @@ public interface MpMaterialMapper extends BaseMapperX<MpMaterialDO> {
.eqIfPresent(MpMaterialDO::getType, pageReqVO.getType())); .eqIfPresent(MpMaterialDO::getType, pageReqVO.getType()));
} }
default List<MpMaterialDO> selectListByMediaId(Collection<String> mediaIds) {
return selectList(MpMaterialDO::getMediaId, mediaIds);
}
} }

View File

@ -9,6 +9,8 @@ import me.chanjar.weixin.common.api.WxConsts;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
import java.util.List;
/** /**
* 公众号素材 Service 接口 * 公众号素材 Service 接口
@ -55,4 +57,12 @@ public interface MpMaterialService {
*/ */
PageResult<MpMaterialDO> getMaterialPage(MpMaterialPageReqVO pageReqVO); PageResult<MpMaterialDO> getMaterialPage(MpMaterialPageReqVO pageReqVO);
/**
* 获得素材列表
*
* @param mediaIds 素材 mediaId 列表
* @return 素材列表
*/
List<MpMaterialDO> getMaterialListByMediaId(Collection<String> mediaIds);
} }

View File

@ -27,6 +27,8 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MATERIAL_UPLOAD_FAIL; import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MATERIAL_UPLOAD_FAIL;
@ -146,6 +148,11 @@ public class MpMaterialServiceImpl implements MpMaterialService {
return mpMaterialMapper.selectPage(pageReqVO); return mpMaterialMapper.selectPage(pageReqVO);
} }
@Override
public List<MpMaterialDO> getMaterialListByMediaId(Collection<String> mediaIds) {
return mpMaterialMapper.selectListByMediaId(mediaIds);
}
/** /**
* 下载微信媒体文件的内容并上传到文件服务 * 下载微信媒体文件的内容并上传到文件服务
* *

View File

@ -8,3 +8,11 @@ export function getFreePublishPage(query) {
params: query params: query
}) })
} }
// 删除公众号素材
export function deleteFreePublish(accountId, articleId) {
return request({
url: '/mp/free-publish/delete?accountId=' + accountId + '&articleId=' + articleId,
method: 'delete'
})
}

View File

@ -1,6 +1,8 @@
<!-- <!--
- Copyright (C) 2018-2019 - Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com - All rights reserved, Designed By www.joolun.com
芋道源码
移除 avue 组件使用 ElementUI 原生组件
--> -->
<template> <template>
<!-- 类型图片 --> <!-- 类型图片 -->

View File

@ -0,0 +1,396 @@
<!--
MIT License
Copyright (c) 2020 www.joolun.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
优化代码和项目的代码保持一致
-->
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<div class="waterfall" v-loading="loading">
<div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list"
:key='item.articleId'>
<wx-news :articles="item.content.newsItem" />
<!-- 操作 -->
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)"
v-hasPermi="['mp:free-publish:delete']" />
</el-row>
</div>
</div>
<!-- 分页组件 -->
<div v-if="list.length <=0 && !loading" class="el-table__empty-block">
<span class="el-table__empty-text">暂无数据</span>
</div>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getFreePublishPage, deleteFreePublish } from "@/api/mp/freePublish";
import { getSimpleAccounts } from "@/api/mp/account";
import WxNews from '@/views/mp/components/wx-news/main.vue';
export default {
name: 'mpDraft',
components: {
WxNews
},
data() {
return {
//
loading: false,
//
showSearch: true,
//
total: 0,
//
list: [],
//
queryParams: {
total: 0, //
currentPage: 1, //
queryParamsSize: 10 //
},
//
accounts: [],
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
//
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
//
this.getList();
})
},
methods: {
/** 查询列表 */
getList() {
//
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询已发表图文')
return false
}
this.loading = true;
getFreePublishPage(this.queryParams).then(response => {
// thumbUrl picUrl wx-news
response.data.list.forEach(item => {
const newsItem = item.content.newsItem;
newsItem.forEach(article => {
article.picUrl = article.thumbUrl;
})
})
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
//
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery();
},
/** 删除按钮操作 */
handleDelete(item){
const articleId = item.articleId;
const accountId = this.queryParams.accountId;
this.$modal.confirm('删除后用户将无法访问此页面,确定删除?').then(function() {
return deleteFreePublish(accountId, articleId);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
}
</script>
<style lang="scss" scoped>
.pagination {
float: right;
margin-right: 25px;
}
.add_but {
padding: 10px;
}
.ope-row {
margin-top: 5px;
text-align: center;
border-top: 1px solid #eaeaea;
padding-top: 5px;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.el-upload__tip {
margin-left: 5px;
}
/*新增图文*/
.left {
display: inline-block;
width: 35%;
vertical-align: top;
margin-top: 200px;
}
.right {
display: inline-block;
width: 60%;
margin-top: -40px;
}
.avatar-uploader {
width: 20%;
display: inline-block;
}
.avatar-uploader .el-upload {
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
text-align: unset !important;
}
.avatar-uploader .el-upload:hover {
border-color: #165dff;
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 230px;
height: 120px;
}
.avatar1 {
width: 120px;
height: 120px;
}
.digest {
width: 60%;
display: inline-block;
vertical-align: top;
}
/*新增图文*/
/*瀑布流样式*/
.waterfall {
width: 100%;
column-gap: 10px;
column-count: 5;
margin: 0 auto;
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
p {
line-height: 30px;
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
column-count: 3;
}
p {
color: red;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
/*瀑布流样式*/
.news-main {
background-color: #FFFFFF;
width: 100%;
margin: auto;
height: 120px;
}
.news-content {
background-color: #acadae;
width: 100%;
height: 120px;
position: relative;
}
.news-content-title {
display: inline-block;
font-size: 15px;
color: #FFFFFF;
position: absolute;
left: 0px;
bottom: 0px;
background-color: black;
width: 98%;
padding: 1%;
opacity: 0.65;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 25px;
}
.news-main-item {
background-color: #FFFFFF;
padding: 5px 0px;
border-top: 1px solid #eaeaea;
width: 100%;
margin: auto;
}
.news-content-item {
position: relative;
margin-left: -3px
}
.news-content-item-title {
display: inline-block;
font-size: 12px;
width: 70%;
}
.news-content-item-img {
display: inline-block;
width: 25%;
background-color: #acadae
}
.input-tt {
padding: 5px;
}
.activeAddNews {
border: 5px solid #2bb673;
}
.news-main-plus {
width: 280px;
text-align: center;
margin: auto;
height: 50px;
}
.icon-plus {
margin: 10px;
font-size: 25px;
}
.select-item {
width: 60%;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.father .child {
display: none;
text-align: center;
position: relative;
bottom: 25px;
}
.father:hover .child {
display: block;
}
.thumb-div {
display: inline-block;
width: 30%;
text-align: center;
}
.thumb-but {
margin: 5px;
}
.material-img {
width: 100%;
height: 100%;
}
</style>