github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mp/material/news.go (about)

     1  package material
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/chanxuehong/wechat/mp/core"
     7  )
     8  
     9  type Article struct {
    10  	ThumbMediaId     string `json:"thumb_media_id"`               // 图文消息的封面图片素材id(必须是永久mediaID)
    11  	Title            string `json:"title"`                        // 标题
    12  	Author           string `json:"author,omitempty"`             // 作者
    13  	Digest           string `json:"digest,omitempty"`             // 图文消息的摘要, 仅有单图文消息才有摘要, 多图文此处为空
    14  	Content          string `json:"content"`                      // 图文消息的具体内容, 支持HTML标签, 必须少于2万字符, 小于1M, 且此处会去除JS
    15  	ContentSourceURL string `json:"content_source_url,omitempty"` // 图文消息的原文地址, 即点击"阅读原文"后的URL
    16  	ShowCoverPic     int    `json:"show_cover_pic"`               // 是否显示封面, 0为false, 即不显示, 1为true, 即显示
    17  	URL              string `json:"url,omitempty"`                // !!!创建时不需要此参数!!! 图文页的URL, 文章创建成功以后, 会由微信自动生成
    18  }
    19  
    20  type News struct {
    21  	Articles []Article `json:"articles,omitempty"`
    22  }
    23  
    24  // 新增永久图文素材.
    25  func AddNews(clt *core.Client, news *News) (mediaId string, err error) {
    26  	const incompleteURL = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token="
    27  
    28  	var result struct {
    29  		core.Error
    30  		MediaId string `json:"media_id"`
    31  	}
    32  	if err = clt.PostJSON(incompleteURL, news, &result); err != nil {
    33  		return
    34  	}
    35  	if result.ErrCode != core.ErrCodeOK {
    36  		err = &result.Error
    37  		return
    38  	}
    39  	mediaId = result.MediaId
    40  	return
    41  }
    42  
    43  // 获取永久图文素材.
    44  func GetNews(clt *core.Client, mediaId string) (news *News, err error) {
    45  	const incompleteURL = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token="
    46  
    47  	var request = struct {
    48  		MediaId string `json:"media_id"`
    49  	}{
    50  		MediaId: mediaId,
    51  	}
    52  	var result struct {
    53  		core.Error
    54  		Articles []Article `json:"news_item"`
    55  	}
    56  	if err = clt.PostJSON(incompleteURL, &request, &result); err != nil {
    57  		return
    58  	}
    59  	if result.ErrCode != core.ErrCodeOK {
    60  		err = &result.Error
    61  		return
    62  	}
    63  	news = &News{
    64  		Articles: result.Articles,
    65  	}
    66  	return
    67  }
    68  
    69  // 修改永久图文素材.
    70  func UpdateNews(clt *core.Client, mediaId string, index int, article *Article) (err error) {
    71  	const incompleteURL = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token="
    72  
    73  	var request = struct {
    74  		MediaId string   `json:"media_id"`
    75  		Index   int      `json:"index"`
    76  		Article *Article `json:"articles,omitempty"`
    77  	}{
    78  		MediaId: mediaId,
    79  		Index:   index,
    80  		Article: article,
    81  	}
    82  	var result core.Error
    83  	if err = clt.PostJSON(incompleteURL, &request, &result); err != nil {
    84  		return
    85  	}
    86  	if result.ErrCode != core.ErrCodeOK {
    87  		err = &result
    88  		return
    89  	}
    90  	return
    91  }
    92  
    93  type BatchGetNewsResult struct {
    94  	TotalCount int        `json:"total_count"` // 该类型的素材的总数
    95  	ItemCount  int        `json:"item_count"`  // 本次调用获取的素材的数量
    96  	Items      []NewsInfo `json:"item"`        // 本次调用获取的素材列表
    97  }
    98  
    99  type NewsInfo struct {
   100  	MediaId    string `json:"media_id"`    // 素材id
   101  	UpdateTime int64  `json:"update_time"` // 最后更新时间
   102  	Content    struct {
   103  		Articles []Article `json:"news_item,omitempty"`
   104  	} `json:"content"`
   105  }
   106  
   107  // 获取图文素材列表.
   108  //
   109  //	offset: 从全部素材的该偏移位置开始返回, 0表示从第一个素材
   110  //	count:  返回素材的数量, 取值在1到20之间
   111  func BatchGetNews(clt *core.Client, offset, count int) (rslt *BatchGetNewsResult, err error) {
   112  	const incompleteURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token="
   113  
   114  	if offset < 0 {
   115  		err = fmt.Errorf("Incorrect offset: %d", offset)
   116  		return
   117  	}
   118  	if count <= 0 {
   119  		err = fmt.Errorf("Incorrect count: %d", count)
   120  		return
   121  	}
   122  
   123  	var request = struct {
   124  		MaterialType string `json:"type"`
   125  		Offset       int    `json:"offset"`
   126  		Count        int    `json:"count"`
   127  	}{
   128  		MaterialType: MaterialTypeNews,
   129  		Offset:       offset,
   130  		Count:        count,
   131  	}
   132  	var result struct {
   133  		core.Error
   134  		BatchGetNewsResult
   135  	}
   136  	if err = clt.PostJSON(incompleteURL, &request, &result); err != nil {
   137  		return
   138  	}
   139  	if result.ErrCode != core.ErrCodeOK {
   140  		err = &result.Error
   141  		return
   142  	}
   143  	rslt = &result.BatchGetNewsResult
   144  	return
   145  }
   146  
   147  // =====================================================================================================================
   148  
   149  // NewsIterator
   150  //
   151  //	iter, err := NewNewsIterator(clt, 0, 10)
   152  //	if err != nil {
   153  //	    // TODO: 增加你的代码
   154  //	}
   155  //
   156  //	for iter.HasNext() {
   157  //	    items, err := iter.NextPage()
   158  //	    if err != nil {
   159  //	        // TODO: 增加你的代码
   160  //	    }
   161  //	    // TODO: 增加你的代码
   162  //	}
   163  type NewsIterator struct {
   164  	clt *core.Client
   165  
   166  	nextOffset int
   167  	count      int
   168  
   169  	lastBatchGetNewsResult *BatchGetNewsResult
   170  	nextPageCalled         bool
   171  }
   172  
   173  func (iter *NewsIterator) TotalCount() int {
   174  	return iter.lastBatchGetNewsResult.TotalCount
   175  }
   176  
   177  func (iter *NewsIterator) HasNext() bool {
   178  	if !iter.nextPageCalled {
   179  		return iter.lastBatchGetNewsResult.ItemCount > 0 || iter.nextOffset < iter.lastBatchGetNewsResult.TotalCount
   180  	}
   181  	return iter.nextOffset < iter.lastBatchGetNewsResult.TotalCount
   182  }
   183  
   184  func (iter *NewsIterator) NextPage() (items []NewsInfo, err error) {
   185  	if !iter.nextPageCalled {
   186  		iter.nextPageCalled = true
   187  		items = iter.lastBatchGetNewsResult.Items
   188  		return
   189  	}
   190  
   191  	rslt, err := BatchGetNews(iter.clt, iter.nextOffset, iter.count)
   192  	if err != nil {
   193  		return
   194  	}
   195  
   196  	iter.lastBatchGetNewsResult = rslt
   197  	iter.nextOffset += rslt.ItemCount
   198  
   199  	items = rslt.Items
   200  	return
   201  }
   202  
   203  func NewNewsIterator(clt *core.Client, offset, count int) (iter *NewsIterator, err error) {
   204  	// 逻辑上相当于第一次调用 NewsIterator.NextPage,
   205  	// 因为第一次调用 NewsIterator.HasNext 需要数据支撑, 所以提前获取了数据
   206  	rslt, err := BatchGetNews(clt, offset, count)
   207  	if err != nil {
   208  		return
   209  	}
   210  
   211  	iter = &NewsIterator{
   212  		clt: clt,
   213  
   214  		nextOffset: offset + rslt.ItemCount,
   215  		count:      count,
   216  
   217  		lastBatchGetNewsResult: rslt,
   218  		nextPageCalled:         false,
   219  	}
   220  	return
   221  }