diff --git a/pom.xml b/pom.xml index d9a57ce6..f49d439c 100644 --- a/pom.xml +++ b/pom.xml @@ -219,6 +219,11 @@ UserAgentUtils 1.20 + + com.google.code.gson + gson + 2.8.0 + diff --git a/yshop-admin/src/main/resources/config/application-dev.yml b/yshop-admin/src/main/resources/config/application-dev.yml index ef94c492..b192f872 100644 --- a/yshop-admin/src/main/resources/config/application-dev.yml +++ b/yshop-admin/src/main/resources/config/application-dev.yml @@ -6,7 +6,7 @@ spring: druid: type: com.alibaba.druid.pool.DruidDataSource driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy - url: jdbc:log4jdbc:mysql://localhost:3306/yshop?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull + url: jdbc:log4jdbc:mysql://localhost:3306/yshopb2c?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull username: root password: root diff --git a/yshop-app/src/main/java/co/yixiang/modules/wechat/rest/controller/WechatController.java b/yshop-app/src/main/java/co/yixiang/modules/wechat/rest/controller/WechatController.java index 06563373..d250832e 100644 --- a/yshop-app/src/main/java/co/yixiang/modules/wechat/rest/controller/WechatController.java +++ b/yshop-app/src/main/java/co/yixiang/modules/wechat/rest/controller/WechatController.java @@ -8,6 +8,7 @@ */ package co.yixiang.modules.wechat.rest.controller; +import cn.binarywang.wx.miniapp.api.WxMaService; import co.yixiang.annotation.AnonymousAccess; import co.yixiang.api.ApiResult; @@ -25,6 +26,7 @@ import co.yixiang.modules.shop.service.YxSystemConfigService; import co.yixiang.modules.user.domain.YxUserRecharge; import co.yixiang.modules.user.service.YxUserRechargeService; +import co.yixiang.mp.config.WxMaConfiguration; import co.yixiang.mp.config.WxMpConfiguration; import co.yixiang.mp.config.WxPayConfiguration; import co.yixiang.utils.BigNum; @@ -111,6 +113,17 @@ public class WechatController { } + /** + * 微信小程序接口能力配置 + */ + @GetMapping("/wxapp/config") + @ApiOperation(value = "微信小程序接口能力配置",notes = "微信小程序接口能力配置") + public boolean wxAppConfig(@RequestParam(value = "signature") String signature, + @RequestParam(value = "timestamp") String timestamp, + @RequestParam(value = "nonce") String nonce) throws WxErrorException { + WxMaService wxService = WxMaConfiguration.getWxMaService(); + return wxService.checkSignature(timestamp,nonce,signature); + } /** * 微信支付/充值回调 diff --git a/yshop-app/src/main/resources/config/application-dev.yml b/yshop-app/src/main/resources/config/application-dev.yml index 19c9ca97..13395177 100644 --- a/yshop-app/src/main/resources/config/application-dev.yml +++ b/yshop-app/src/main/resources/config/application-dev.yml @@ -5,7 +5,7 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://localhost:3306/yshop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8 + url: jdbc:mysql://localhost:3306/yshopb2c?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8 username: root password: root # 从库数据源 diff --git a/yshop-common/src/main/java/co/yixiang/constant/ShopConstants.java b/yshop-common/src/main/java/co/yixiang/constant/ShopConstants.java index aeb5666b..ec13f290 100644 --- a/yshop-common/src/main/java/co/yixiang/constant/ShopConstants.java +++ b/yshop-common/src/main/java/co/yixiang/constant/ShopConstants.java @@ -53,8 +53,10 @@ public interface ShopConstants { * 微信公众号service */ String YSHOP_WEIXIN_MP_SERVICE = "yshop_weixin_mp_service"; - - + /** + * 微信小程序service + */ + String YSHOP_WEIXIN_MA_SERVICE = "yshop_weixin_ma_service"; /** * 商城默认密码 @@ -166,4 +168,6 @@ public interface ShopConstants { String YSHOP_REDIS_CITY_KEY = "yshop:city"; String YSHOP_APP_LOGIN_USER = "app-online-token"; + + } diff --git a/yshop-common/src/main/java/co/yixiang/constant/SystemConfigConstants.java b/yshop-common/src/main/java/co/yixiang/constant/SystemConfigConstants.java index 6a9b60ea..635d4c5a 100644 --- a/yshop-common/src/main/java/co/yixiang/constant/SystemConfigConstants.java +++ b/yshop-common/src/main/java/co/yixiang/constant/SystemConfigConstants.java @@ -37,6 +37,8 @@ public class SystemConfigConstants { public final static String WECHAT_SHARE_TITLE="wechat_share_title"; public final static String WECHAT_SOURCEID="wechat_sourceid"; public final static String WECHAT_TOKEN="wechat_token"; + public final static String WECHAT_MA_TOKEN="wechat_ma_token"; + public final static String WECHAT_MA_ENCODINGAESKEY="wechat_ma_encodingaeskey"; public final static String WECHAT_TYPE="wechat_type"; public final static String WXAPP_APPID="wxapp_appId"; public final static String WXAPP_SECRET="wxapp_secret"; diff --git a/yshop-common/src/main/java/co/yixiang/utils/GsonParser.java b/yshop-common/src/main/java/co/yixiang/utils/GsonParser.java new file mode 100644 index 00000000..c62bb9bb --- /dev/null +++ b/yshop-common/src/main/java/co/yixiang/utils/GsonParser.java @@ -0,0 +1,26 @@ +package co.yixiang.utils; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; + +import java.io.Reader; + +/** + * @author niefy + */ +public class GsonParser { + private static final JsonParser JSON_PARSER = new JsonParser(); + + public static JsonObject parse(String json) { + return JSON_PARSER.parse(json).getAsJsonObject(); + } + + public static JsonObject parse(Reader json) { + return JSON_PARSER.parse(json).getAsJsonObject(); + } + + public static JsonObject parse(JsonReader json) { + return JSON_PARSER.parse(json).getAsJsonObject(); + } +} diff --git a/yshop-common/src/main/java/co/yixiang/utils/ShopKeyUtils.java b/yshop-common/src/main/java/co/yixiang/utils/ShopKeyUtils.java index d0911a65..5ee41875 100644 --- a/yshop-common/src/main/java/co/yixiang/utils/ShopKeyUtils.java +++ b/yshop-common/src/main/java/co/yixiang/utils/ShopKeyUtils.java @@ -145,6 +145,21 @@ public class ShopKeyUtils { return wxAppSecret+getExtendValue(); } + /** + * 微信公众号验证token + */ + public static String getWechatMaToken(){ + String wechatToken= SystemConfigConstants.WECHAT_MA_TOKEN; + return wechatToken+getExtendValue(); + } + /** + * 微信公众号 EncodingAESKey + */ + public static String getWechatMaEncodingAESKey(){ + String wechatEncodingAESKey= SystemConfigConstants.WECHAT_MA_ENCODINGAESKEY; + return wechatEncodingAESKey+getExtendValue(); + } + /** * 支付appId */ @@ -153,4 +168,13 @@ public class ShopKeyUtils { return wxNativeAppAppId+getExtendValue(); } + /** + * 微信小程序service + * @return + */ + public static String getYshopWeiXinMaSevice() { + String yshopWeiXinMaSevice= ShopConstants.YSHOP_WEIXIN_MA_SERVICE; + return yshopWeiXinMaSevice+getExtendValue(); + } + } diff --git a/yshop-shop/src/main/java/co/yixiang/modules/shop/rest/SystemConfigController.java b/yshop-shop/src/main/java/co/yixiang/modules/shop/rest/SystemConfigController.java index 5c755cef..9267ae86 100644 --- a/yshop-shop/src/main/java/co/yixiang/modules/shop/rest/SystemConfigController.java +++ b/yshop-shop/src/main/java/co/yixiang/modules/shop/rest/SystemConfigController.java @@ -16,6 +16,7 @@ import co.yixiang.modules.aop.ForbidSubmit; import co.yixiang.modules.shop.domain.YxSystemConfig; import co.yixiang.modules.shop.service.YxSystemConfigService; import co.yixiang.modules.shop.service.dto.YxSystemConfigQueryCriteria; +import co.yixiang.mp.config.WxMaConfiguration; import co.yixiang.mp.config.WxMpConfiguration; import co.yixiang.mp.config.WxPayConfiguration; import co.yixiang.utils.RedisUtil; @@ -77,6 +78,7 @@ public class SystemConfigController { if(SystemConfigConstants.WECHAT_APPID.equals(key)){ WxMpConfiguration.removeWxMpService(); WxPayConfiguration.removeWxPayService(); + WxMaConfiguration.removeWxMaService(); } if(SystemConfigConstants.WXPAY_MCHID.equals(key) || SystemConfigConstants.WXAPP_APPID.equals(key)){ WxPayConfiguration.removeWxPayService(); diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/bean/WxMaLiveInfo.java b/yshop-weixin/src/main/java/co/yixiang/mp/bean/WxMaLiveInfo.java new file mode 100644 index 00000000..a141934b --- /dev/null +++ b/yshop-weixin/src/main/java/co/yixiang/mp/bean/WxMaLiveInfo.java @@ -0,0 +1,56 @@ +package co.yixiang.mp.bean; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +/** + * 直播接口入参 + */ +@Data +public class WxMaLiveInfo implements Serializable { + private static final long serialVersionUID = 7285263767524755887L; + + /** + * 直播列表 + */ + @Data + public static class RoomInfo implements Serializable { + private static final long serialVersionUID = 7745775280267417154L; + private String name; + private Integer roomid; + private String coverImg; + private String shareImg; + private Integer liveStatus; + private Long startTime; + private Long endTime; + private String anchorName; + private String anchorWechat; + private String anchorImg; + private Integer type; + private Integer screenType; + private Integer closeLike; + private Integer closeGoods; + private Integer closeComment; + private List goods; + } + + /** + * 商品列表 + */ + @Data + public static class Goods implements Serializable { + private static final long serialVersionUID = 5769245932149287574L; + private Integer goodsId; + private String coverImgUrl; + private String url; + private Integer priceType; + private String price; + private String price2; + private String name; + /** + * 1, 2:表示是为api添加商品,否则是在MP添加商品 + */ + private String thirdPartyTag; + } +} diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/bean/WxMaLiveResult.java b/yshop-weixin/src/main/java/co/yixiang/mp/bean/WxMaLiveResult.java new file mode 100644 index 00000000..403b1783 --- /dev/null +++ b/yshop-weixin/src/main/java/co/yixiang/mp/bean/WxMaLiveResult.java @@ -0,0 +1,129 @@ +package co.yixiang.mp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +/** + *
+ * 直播间操作返回结果
+ * Created by lipengjun on 2020/6/29.
+ * 
+ * + * @author lipengjun (939961241@qq.com) + */ +@Data +public class WxMaLiveResult implements Serializable { + private static final long serialVersionUID = 1L; + private Integer errcode; + private String errmsg; + private Integer total; + private Integer auditId; + private Integer goodsId; + private List goods; + + /** + * 直播间列表 + */ + @SerializedName("room_info") + private List roomInfos; + + /** + * 获取回放源视频列表 + */ + @SerializedName("live_replay") + private List liveReplay; + + public static WxMaLiveResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaLiveResult.class); + } + + /** + * 商品列表 + */ + @Data + public static class Goods implements Serializable { + private static final long serialVersionUID = 5769245932149287574L; + @SerializedName("goods_id") + private Integer goodsId; + /** + * 获取商品列表返回的商品图片 + */ + @SerializedName("cover_img_url") + private String coverImgUrl; + /** + * 获取直播间列表返回的商品图片 + */ + @SerializedName("cover_img") + private String coverImg; + private String name; + private String url; + @SerializedName("price_type") + private Integer priceType; + /** + * 0:未审核,1:审核中,2:审核通过,3审核失败 + */ + @SerializedName("audit_status") + private Integer auditStatus; + private String price; + private String price2; + /** + * 1, 2:表示是为api添加商品,否则是在MP添加商品 + */ + @SerializedName("third_party_tag") + private String thirdPartyTag; + } + + /** + * 直播列表 + */ + @Data + public static class RoomInfo implements Serializable { + private static final long serialVersionUID = 7745775280267417154L; + private String name; + @SerializedName("roomid") + private Integer roomId; + @SerializedName("cover_img") + private String coverImg; + @SerializedName("share_img") + private String shareImg; + @SerializedName("live_status") + private Integer liveStatus; + @SerializedName("start_time") + private Long startTime; + @SerializedName("end_time") + private Long endTime; + @SerializedName("anchor_name") + private String anchorName; + @SerializedName("anchor_wechat") + private String anchorWechat; + @SerializedName("anchor_img") + private String anchorImg; + private Integer type; + @SerializedName("screen_type") + private Integer screenType; + @SerializedName("close_like") + private Integer closeLike; + @SerializedName("closeGoods") + private Integer closeGoods; + @SerializedName("close_comment") + private Integer closeComment; + private List goods; + } + + /** + * 回放数据列表 + */ + @Data + public static class LiveReplay implements Serializable { + private static final long serialVersionUID = 7683927205627536320L; + @SerializedName("expire_time") + private String expireTime; + @SerializedName("create_time") + private String createTime; + @SerializedName("media_url") + private String mediaUrl; + } +} diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/config/WxMaConfiguration.java b/yshop-weixin/src/main/java/co/yixiang/mp/config/WxMaConfiguration.java new file mode 100644 index 00000000..2534bcaf --- /dev/null +++ b/yshop-weixin/src/main/java/co/yixiang/mp/config/WxMaConfiguration.java @@ -0,0 +1,71 @@ +package co.yixiang.mp.config; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import cn.binarywang.wx.miniapp.message.WxMaMessageHandler; +import cn.binarywang.wx.miniapp.message.WxMaMessageRouter; +import co.yixiang.mp.handler.LogHandler; +import co.yixiang.utils.RedisUtil; +import co.yixiang.utils.RedisUtils; +import co.yixiang.utils.ShopKeyUtils; +import com.google.common.collect.Maps; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import java.util.Map; + +/** + * @author Binary Wang + */ +@Configuration +public class WxMaConfiguration { + private static Map maServices = Maps.newHashMap(); + private static Map routers = Maps.newHashMap(); + private static RedisUtils redisUtils; + private static WxMaMessageHandler wxMaMessageHandler; + public static WxMaMessageRouter getRouter(String appid) { + return routers.get(appid); + } + @Autowired + public WxMaConfiguration(RedisUtils redisUtils, LogHandler logHandler) { + this.redisUtils = redisUtils; + this.wxMaMessageHandler = wxMaMessageHandler; + } + + public static WxMaService getWxMaService() { + WxMaService wxMaService = maServices.get(ShopKeyUtils.getYshopWeiXinMaSevice()); + //增加一个redis标识 + if(wxMaService == null || redisUtils.get(ShopKeyUtils.getYshopWeiXinMaSevice()) == null){ + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); + config.setMsgDataFormat("JSON"); + config.setAppid(RedisUtil.get(ShopKeyUtils.getWxAppAppId())); + config.setSecret(RedisUtil.get(ShopKeyUtils.getWxAppSecret())); + config.setToken(RedisUtil.get(ShopKeyUtils.getWechatMaToken())); + config.setAesKey(RedisUtil.get(ShopKeyUtils.getWechatMaEncodingAESKey())); + WxMaService service = new WxMaServiceImpl(); + maServices.put(ShopKeyUtils.getYshopWeiXinMaSevice(), wxMaService); + routers.put(ShopKeyUtils.getYshopWeiXinMaSevice(), newRouter(wxMaService)); + //增加标识 + redisUtils.set(ShopKeyUtils.getYshopWeiXinMaSevice(),"yshop"); + service.setWxMaConfig(config); + } + return wxMaService; + } + /** + * 移除WxMpService + */ + public static void removeWxMaService(){ + redisUtils.del(ShopKeyUtils.getYshopWeiXinMaSevice()); + maServices.remove(ShopKeyUtils.getYshopWeiXinMaSevice()); + routers.remove(ShopKeyUtils.getYshopWeiXinMaSevice()); + } + private static WxMaMessageRouter newRouter(WxMaService service) { + final WxMaMessageRouter router = new WxMaMessageRouter(service); + router.rule().handler(wxMaMessageHandler).next(); + return router; + } + + + +} + diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/service/WxMaLiveGoodsService.java b/yshop-weixin/src/main/java/co/yixiang/mp/service/WxMaLiveGoodsService.java new file mode 100644 index 00000000..a68f24da --- /dev/null +++ b/yshop-weixin/src/main/java/co/yixiang/mp/service/WxMaLiveGoodsService.java @@ -0,0 +1,124 @@ +package co.yixiang.mp.service; + +import co.yixiang.mp.bean.WxMaLiveInfo; +import co.yixiang.mp.bean.WxMaLiveResult; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + *
+ * 直播间商品相关操作接口
+ * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/liveplayer/commodity-api.html
+ * Created by lipengjun on 2020/6/29.
+ * 
+ * + * @author lipengjun (939961241@qq.com) + */ +public interface WxMaLiveGoodsService { + String ADD_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/add"; + String RESET_AUDIT_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/resetaudit"; + String AUDIT_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/audit"; + String DELETE_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/delete"; + String UPDATE_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/update"; + String GET_GOODS_WARE_HOUSE = "https://api.weixin.qq.com/wxa/business/getgoodswarehouse"; + String GET_APPROVED_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved"; + + /** + * 商品添加并提审 + *
+     * 调用此接口上传并提审需要直播的商品信息,审核通过后商品录入【小程序直播】商品库
+     * 注意:开发者必须保存【商品ID】与【审核单ID】,如果丢失,则无法调用其他相关接口
+     * 调用额度:500次/一天
+     * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/add?access_token=ACCESS_TOKEN
+     * 
+ * + * @param goods 商品 + * @return 返回auditId、goodsId + * @throws WxErrorException . + */ + WxMaLiveResult addGoods(WxMaLiveInfo.Goods goods) throws WxErrorException; + + /** + * 撤回审核 + *
+     * 调用此接口,可撤回直播商品的提审申请,消耗的提审次数不返还
+     * 调用额度:500次/一天
+     * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/resetaudit?access_token=ACCESS_TOKEN
+     * 
+     * @param auditId 审核单ID
+     * @param goodsId 商品ID
+     * @return 撤回审核是否成功
+     * @throws WxErrorException .
+     */
+    boolean resetAudit(Integer auditId, Integer goodsId) throws WxErrorException;
+
+    /**
+     * 重新提交审核
+     * 
+     * 调用此接口,可撤回直播商品的提审申请,消耗的提审次数不返还
+     * 调用额度:500次/一天(与接口'商品添加并提审'共用500次限制)
+     * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/audit?access_token=ACCESS_TOKEN
+     * 
+     * @param goodsId 商品ID
+     * @return 审核单ID
+     * @throws WxErrorException .
+     */
+    String auditGoods(Integer goodsId) throws WxErrorException;
+
+    /**
+     * 删除商品
+     * 
+     * 调用此接口,可删除【小程序直播】商品库中的商品,删除后直播间上架的该商品也将被同步删除,不可恢复;
+     * 调用额度:1000次/一天
+     * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/delete?access_token=ACCESS_TOKEN
+     * 
+     * @param goodsId 商品ID
+     * @return 删除商品是否成功
+     * @throws WxErrorException .
+     */
+    boolean deleteGoods(Integer goodsId) throws WxErrorException;
+
+    /**
+     * 更新商品
+     * 
+     * 调用此接口可以更新商品信息,审核通过的商品仅允许更新价格类型与价格,审核中的商品不允许更新,未审核的商品允许更新所有字段, 只传入需要更新的字段。
+     * 调用额度:1000次/一天
+     * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/update?access_token=ACCESS_TOKEN
+     * 
+ * + * @param goods 商品 + * @return 更新商品是否成功 + * @throws WxErrorException . + */ + boolean updateGoods(WxMaLiveInfo.Goods goods) throws WxErrorException; + + /** + * 获取商品状态 + *
+     * 调用此接口可获取商品的信息与审核状态
+     * 调用额度:1000次/一天
+     * http请求方式:POST https://api.weixin.qq.com/wxa/business/getgoodswarehouse?access_token=ACCESS_TOKEN
+     * 
+     * @param goodsIds 商品ID集
+     * @return 商品状态信息
+     * @throws WxErrorException .
+     */
+    WxMaLiveResult getGoodsWareHouse(List goodsIds) throws WxErrorException;
+
+    /**
+     * 获取商品列表
+     * 
+     * 调用此接口可获取商品列表
+     * 调用额度:10000次/一天
+     * http请求方式:GET https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved?access_token=ACCESS_TOKEN
+     * 
+     * @param offset 分页条数起点
+     * @param limit  分页大小,默认30,不超过100
+     * @param status 商品状态,0:未审核。1:审核中,2:审核通过,3:审核驳回
+     * @return 商品列表
+     * @throws WxErrorException .
+     */
+    WxMaLiveResult getApprovedGoods(Integer offset, Integer limit, Integer status) throws WxErrorException;
+}
+
diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/service/WxMaLiveService.java b/yshop-weixin/src/main/java/co/yixiang/mp/service/WxMaLiveService.java
new file mode 100644
index 00000000..026718ff
--- /dev/null
+++ b/yshop-weixin/src/main/java/co/yixiang/mp/service/WxMaLiveService.java
@@ -0,0 +1,94 @@
+package co.yixiang.mp.service;
+
+import co.yixiang.mp.bean.WxMaLiveInfo;
+import co.yixiang.mp.bean.WxMaLiveResult;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import java.util.List;
+
+/**
+ * 
+ *  直播相关操作接口.
+ *  Created by yjwang on 2020/4/5.
+ * 
+ * + * @author yjwang + */ +public interface WxMaLiveService { + String GET_LIVE_INFO = "https://api.weixin.qq.com/wxa/business/getliveinfo"; + String CREATE_ROOM = "https://api.weixin.qq.com/wxaapi/broadcast/room/create"; + String ADD_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/room/addgoods"; + + /** + * 创建直播间 + *
+     * 调用此接口创建直播间,创建成功后将在直播间列表展示,调用额度:10000次/一天
+     * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/liveplayer/studio-api.html#1
+     * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/create?access_token=ACCESS_TOKEN
+     * 
+ * + * @param roomInfo 直播间信息 + * @return . + * @throws WxErrorException . + */ + Integer createRoom(WxMaLiveInfo.RoomInfo roomInfo) throws WxErrorException; + + /** + * 获取直播房间列表.(分页) + * + * @param start 起始拉取房间,start = 0 表示从第 1 个房间开始拉取 + * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 + * @return . + * @throws WxErrorException . + */ + WxMaLiveResult getLiveInfo(Integer start, Integer limit) throws WxErrorException; + + /** + * 获取所有直播间信息(没有分页直接获取全部) + * + * @return . + * @throws WxErrorException . + */ + List getLiveInfos() throws WxErrorException; + + /** + * 获取直播房间回放数据信息. + * + * @param action 获取回放 + * @param roomId 直播间 id + * @param start 起始拉取视频,start = 0 表示从第 1 个视频片段开始拉取 + * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 + * @return . + * @throws WxErrorException . + */ + WxMaLiveResult getLiveReplay(String action, Integer roomId, Integer start, Integer limit) throws WxErrorException; + + /** + * 获取直播房间回放数据信息. + *

+ * 获取回放 (默认:get_replay) + * + * @param roomId 直播间 id + * @param start 起始拉取视频,start = 0 表示从第 1 个视频片段开始拉取 + * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 + * @return . + * @throws WxErrorException . + */ + WxMaLiveResult getLiveReplay(Integer roomId, Integer start, Integer limit) throws WxErrorException; + + /** + * 直播间导入商品 + *

+ * 调用接口往指定直播间导入已入库的商品 + * 调用频率 + * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/addgoods?access_token=ACCESS_TOKEN + *

+     * @param roomId 房间ID
+     * @param goodsIds 数组列表,可传入多个,里面填写 商品 ID
+     * @return 导入商品是否成功
+     * @throws WxErrorException .
+     */
+    boolean addGoodsToRoom(Integer roomId, List goodsIds) throws WxErrorException;
+}
diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/service/impl/WxMaLiveGoodsServiceImpl.java b/yshop-weixin/src/main/java/co/yixiang/mp/service/impl/WxMaLiveGoodsServiceImpl.java
new file mode 100644
index 00000000..2141c509
--- /dev/null
+++ b/yshop-weixin/src/main/java/co/yixiang/mp/service/impl/WxMaLiveGoodsServiceImpl.java
@@ -0,0 +1,130 @@
+package co.yixiang.mp.service.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import co.yixiang.mp.bean.WxMaLiveInfo;
+import co.yixiang.mp.bean.WxMaLiveResult;
+import co.yixiang.mp.config.WxMaConfiguration;
+import co.yixiang.mp.service.WxMaLiveGoodsService;
+import co.yixiang.utils.GsonParser;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@AllArgsConstructor
+public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
+
+    @Override
+    public WxMaLiveResult addGoods(WxMaLiveInfo.Goods goods) throws WxErrorException {
+        Map map = new HashMap<>(2);
+        map.put("goodsInfo", goods);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.post(ADD_GOODS, WxMaGsonBuilder.create().toJson(map));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        return WxMaLiveResult.fromJson(jsonObject.toString());
+    }
+
+    @Override
+    public boolean resetAudit(Integer auditId, Integer goodsId) throws WxErrorException {
+        Map map = new HashMap<>(4);
+        map.put("auditId", auditId);
+        map.put("goodsId", goodsId);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.post(RESET_AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        return true;
+    }
+
+    @Override
+    public String auditGoods(Integer goodsId) throws WxErrorException {
+        Map map = new HashMap<>(2);
+        map.put("goodsId", goodsId);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.post(AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        return jsonObject.get("auditId").getAsString();
+    }
+
+    @Override
+    public boolean deleteGoods(Integer goodsId) throws WxErrorException {
+        Map map = new HashMap<>(2);
+        map.put("goodsId", goodsId);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.post(DELETE_GOODS, WxMaGsonBuilder.create().toJson(map));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updateGoods(WxMaLiveInfo.Goods goods) throws WxErrorException {
+        Map map = new HashMap<>(2);
+        map.put("goodsInfo", goods);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.post(UPDATE_GOODS, WxMaGsonBuilder.create().toJson(map));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        return true;
+    }
+
+    @Override
+    public WxMaLiveResult getGoodsWareHouse(List goodsIds) throws WxErrorException {
+        Map map = new HashMap<>(2);
+        map.put("goods_ids", goodsIds);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.post(GET_GOODS_WARE_HOUSE, WxMaGsonBuilder.create().toJson(map));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        return WxMaLiveResult.fromJson(jsonObject.toString());
+    }
+
+    @Override
+    public WxMaLiveResult getApprovedGoods(Integer offset, Integer limit, Integer status) throws WxErrorException {
+        ImmutableMap params = ImmutableMap.of("status", status, "offset", offset, "limit", limit);
+        WxMaService wxMaService = WxMaConfiguration.getWxMaService();
+        String responseContent = wxMaService.get(GET_APPROVED_GOODS, Joiner.on("&").withKeyValueSeparator("=").join(params));
+        JsonObject jsonObject = GsonParser.parse(responseContent);
+        if (jsonObject.get("errcode").getAsInt() != 0) {
+            throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+        JsonArray goodsArr = jsonObject.getAsJsonArray("goods");
+        if (goodsArr.size() > 0) {
+            for (int i = 0; i < goodsArr.size(); i++) {
+                // 接口返回key是驼峰
+                JsonObject goods = (JsonObject) goodsArr.get(i);
+                goods.addProperty("goods_id", goods.get("goodsId").getAsInt());
+                goods.addProperty("cover_img_url", goods.get("coverImgUrl").getAsString());
+                goods.addProperty("price_type", goods.get("priceType").getAsInt());
+                goods.addProperty("third_party_tag", goods.get("thirdPartyTag").getAsInt());
+                goods.addProperty("audit_status", status);
+            }
+        }
+        return WxMaLiveResult.fromJson(jsonObject.toString());
+    }
+
+}
diff --git a/yshop-weixin/src/main/java/co/yixiang/mp/service/impl/WxMaLiveServiceImpl.java b/yshop-weixin/src/main/java/co/yixiang/mp/service/impl/WxMaLiveServiceImpl.java
new file mode 100644
index 00000000..88b8a0c8
--- /dev/null
+++ b/yshop-weixin/src/main/java/co/yixiang/mp/service/impl/WxMaLiveServiceImpl.java
@@ -0,0 +1,121 @@
+package co.yixiang.mp.service.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import co.yixiang.mp.bean.WxMaLiveInfo;
+import co.yixiang.mp.bean.WxMaLiveResult;
+import co.yixiang.mp.config.WxMaConfiguration;
+import co.yixiang.mp.service.WxMaLiveService;
+import co.yixiang.utils.GsonParser;
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 
+ *  Created by yjwang on 2020/4/5.
+ * 
+ * + * @author yjwang + */ +@Slf4j +@AllArgsConstructor +public class WxMaLiveServiceImpl implements WxMaLiveService { + + + @Override + public Integer createRoom(WxMaLiveInfo.RoomInfo roomInfo) throws WxErrorException { + WxMaService wxMaService = WxMaConfiguration.getWxMaService(); + String responseContent = wxMaService.post(CREATE_ROOM, WxMaGsonBuilder.create().toJson(roomInfo)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return jsonObject.get("roomId").getAsInt(); + } + + @Override + public WxMaLiveResult getLiveInfo(Integer start, Integer limit) throws WxErrorException { + JsonObject jsonObject = getLiveInfo(start, limit, null); + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + + @Override + public List getLiveInfos() throws WxErrorException { + List results = new ArrayList<>(); + int start = 0; + Integer limit = 80; + Integer total = 0; + WxMaLiveResult liveInfo; + do { + if (total != 0 && total <= start) { + break; + } + liveInfo = getLiveInfo(start, limit); + if (liveInfo == null) { + return null; + } + results.addAll(liveInfo.getRoomInfos()); + total = liveInfo.getTotal(); + start = results.size(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + log.error("InterruptedException", e); + } + } while (results.size() <= total); + + return results; + } + + @Override + public WxMaLiveResult getLiveReplay(String action, Integer roomId, Integer start, Integer limit) throws WxErrorException { + Map map = new HashMap<>(4); + map.put("action", action); + map.put("room_id", roomId); + JsonObject jsonObject = getLiveInfo(start, limit, map); + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + + @Override + public WxMaLiveResult getLiveReplay(Integer roomId, Integer start, Integer limit) throws WxErrorException { + return getLiveReplay("get_replay", roomId, start, limit); + } + + @Override + public boolean addGoodsToRoom(Integer roomId, List goodsIds) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("roomId", roomId); + map.put("ids", goodsIds); + WxMaService wxMaService = WxMaConfiguration.getWxMaService(); + String responseContent = wxMaService.post(ADD_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return true; + } + + private JsonObject getLiveInfo(Integer start, Integer limit, Map map) throws WxErrorException { + if (map == null) { + map = new HashMap(2); + } + map.put("start", start); + map.put("limit", limit); + WxMaService wxMaService = WxMaConfiguration.getWxMaService(); + String responseContent = wxMaService.post(GET_LIVE_INFO, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return jsonObject; + } +}