| @@ -2,6 +2,7 @@ package me.chanjar.weixin.common.error; | |||||
| import lombok.Builder; | import lombok.Builder; | ||||
| import lombok.Data; | import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | |||||
| import me.chanjar.weixin.common.WxType; | import me.chanjar.weixin.common.WxType; | ||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
| @@ -17,6 +18,7 @@ import java.io.Serializable; | |||||
| * @author Daniel Qian & Binary Wang | * @author Daniel Qian & Binary Wang | ||||
| */ | */ | ||||
| @Data | @Data | ||||
| @NoArgsConstructor | |||||
| @Builder | @Builder | ||||
| public class WxError implements Serializable { | public class WxError implements Serializable { | ||||
| private static final long serialVersionUID = 7869786563361406291L; | private static final long serialVersionUID = 7869786563361406291L; | ||||
| @@ -39,6 +41,11 @@ public class WxError implements Serializable { | |||||
| private String json; | private String json; | ||||
| public WxError(int errorCode, String errorMsg) { | |||||
| this.errorCode = errorCode; | |||||
| this.errorMsg = errorMsg; | |||||
| } | |||||
| public static WxError fromJson(String json) { | public static WxError fromJson(String json) { | ||||
| return fromJson(json, null); | return fromJson(json, null); | ||||
| } | } | ||||
| @@ -0,0 +1,65 @@ | |||||
| package me.chanjar.weixin.open.api; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.open.bean.ma.privacy.GetPrivacySettingResult; | |||||
| import me.chanjar.weixin.open.bean.ma.privacy.SetPrivacySetting; | |||||
| import me.chanjar.weixin.open.bean.ma.privacy.UploadPrivacyFileResult; | |||||
| import org.jetbrains.annotations.Nullable; | |||||
| /** | |||||
| * 微信第三方平台 小程序用户隐私保护指引接口 | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| public interface WxOpenMaPrivacyService { | |||||
| /** | |||||
| * 1 设置小程序用户隐私保护指引 | |||||
| */ | |||||
| String OPEN_SET_PRIVACY_SETTING = "https://api.weixin.qq.com/cgi-bin/component/setprivacysetting"; | |||||
| /** | |||||
| * 2 查询小程序用户隐私保护指引 | |||||
| */ | |||||
| String OPEN_GET_PRIVACY_SETTING = "https://api.weixin.qq.com/cgi-bin/component/getprivacysetting"; | |||||
| /** | |||||
| * 3 上传小程序用户隐私保护指引文件 | |||||
| */ | |||||
| String OPEN_UPLOAD_PRIVACY_FILE = "https://api.weixin.qq.com/cgi-bin/component/uploadprivacyextfile"; | |||||
| /** | |||||
| * 查询小程序用户隐私保护指引 | |||||
| * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/get_privacy_setting.html | |||||
| * | |||||
| * @param privacyVer 1表示现网版本,即,传1则该接口返回的内容是现网版本的;2表示开发版,即,传2则该接口返回的内容是开发版本的。默认是2。 | |||||
| * @return 查询结果 | |||||
| * @throws WxErrorException 如果出错,抛出此异常 | |||||
| */ | |||||
| GetPrivacySettingResult getPrivacySetting(@Nullable Integer privacyVer) throws WxErrorException; | |||||
| /** | |||||
| * 设置小程序用户隐私保护指引 | |||||
| * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html | |||||
| * | |||||
| * @param dto 参数对象 | |||||
| * @throws WxErrorException 如果出错,抛出此异常 | |||||
| */ | |||||
| void setPrivacySetting(SetPrivacySetting dto) throws WxErrorException; | |||||
| /** | |||||
| * 上传小程序用户隐私保护指引文件 | |||||
| * 本接口用于上传自定义的小程序的用户隐私保护指引 | |||||
| * 仅限文本文件, 限制文件大小为不超过100kb,否则会报错 | |||||
| * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/upload_privacy_exfile.html | |||||
| * | |||||
| * @param content 文本文件内容 | |||||
| * @return 上传结果 | |||||
| * @throws WxErrorException 如果出错,抛出此异常 | |||||
| */ | |||||
| UploadPrivacyFileResult uploadPrivacyFile(String content) throws WxErrorException; | |||||
| } | |||||
| @@ -476,4 +476,11 @@ public interface WxOpenMaService extends WxMaService { | |||||
| * (5)发布已设置的二维码规则 | * (5)发布已设置的二维码规则 | ||||
| */ | */ | ||||
| WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException; | WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException; | ||||
| /** | |||||
| * 小程序用户隐私保护指引服务 | |||||
| * | |||||
| * @return 小程序用户隐私保护指引服务 | |||||
| */ | |||||
| WxOpenMaPrivacyService getPrivacyService(); | |||||
| } | } | ||||
| @@ -0,0 +1,51 @@ | |||||
| package me.chanjar.weixin.open.api.impl; | |||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
| import lombok.AllArgsConstructor; | |||||
| import lombok.SneakyThrows; | |||||
| import me.chanjar.weixin.common.error.WxError; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.open.api.WxOpenMaPrivacyService; | |||||
| import me.chanjar.weixin.open.bean.ma.privacy.GetPrivacySettingResult; | |||||
| import me.chanjar.weixin.open.bean.ma.privacy.SetPrivacySetting; | |||||
| import me.chanjar.weixin.open.bean.ma.privacy.UploadPrivacyFileResult; | |||||
| import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; | |||||
| import org.jetbrains.annotations.Nullable; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * 微信第三方平台 小程序用户隐私保护指引接口 | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| @AllArgsConstructor | |||||
| public class WxOpenMaPrivacyServiceImpl implements WxOpenMaPrivacyService { | |||||
| private final WxMaService wxMaService; | |||||
| @Override | |||||
| public GetPrivacySettingResult getPrivacySetting(@Nullable Integer privacyVer) throws WxErrorException { | |||||
| Map<String, Object> params = new HashMap<>(); | |||||
| if (privacyVer != null) { | |||||
| params.put("privacy_ver", privacyVer); | |||||
| } | |||||
| String json = wxMaService.post(OPEN_GET_PRIVACY_SETTING, params); | |||||
| return WxOpenGsonBuilder.create().fromJson(json, GetPrivacySettingResult.class); | |||||
| } | |||||
| @Override | |||||
| public void setPrivacySetting(SetPrivacySetting dto) throws WxErrorException { | |||||
| wxMaService.post(OPEN_SET_PRIVACY_SETTING, dto); | |||||
| } | |||||
| @SneakyThrows | |||||
| @Override | |||||
| public UploadPrivacyFileResult uploadPrivacyFile(String content) throws WxErrorException { | |||||
| throw new WxErrorException(new WxError(5003, "暂未实现用户隐私指引内容上传")); | |||||
| } | |||||
| } | |||||
| @@ -6,8 +6,10 @@ import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | ||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
| import lombok.Getter; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
| import me.chanjar.weixin.open.api.WxOpenComponentService; | import me.chanjar.weixin.open.api.WxOpenComponentService; | ||||
| import me.chanjar.weixin.open.api.WxOpenMaPrivacyService; | |||||
| import me.chanjar.weixin.open.api.WxOpenMaService; | import me.chanjar.weixin.open.api.WxOpenMaService; | ||||
| import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo; | import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo; | ||||
| import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; | import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; | ||||
| @@ -33,10 +35,14 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | |||||
| private WxMaConfig wxMaConfig; | private WxMaConfig wxMaConfig; | ||||
| private String appId; | private String appId; | ||||
| @Getter | |||||
| private final WxOpenMaPrivacyService privacyService; | |||||
| public WxOpenMaServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMaConfig wxMaConfig) { | public WxOpenMaServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMaConfig wxMaConfig) { | ||||
| this.wxOpenComponentService = wxOpenComponentService; | this.wxOpenComponentService = wxOpenComponentService; | ||||
| this.appId = appId; | this.appId = appId; | ||||
| this.wxMaConfig = wxMaConfig; | this.wxMaConfig = wxMaConfig; | ||||
| this.privacyService = new WxOpenMaPrivacyServiceImpl(this); | |||||
| initHttp(); | initHttp(); | ||||
| } | } | ||||
| @@ -0,0 +1,118 @@ | |||||
| package me.chanjar.weixin.open.bean.ma.privacy; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import lombok.Data; | |||||
| import lombok.Getter; | |||||
| import lombok.Setter; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenResult; | |||||
| import java.util.List; | |||||
| /** | |||||
| * 查询小程序用户隐私保护指引 响应 | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public class GetPrivacySettingResult extends WxOpenResult { | |||||
| /** | |||||
| * 代码是否存在, 0 不存在, 1 存在 。如果最近没有通过commit接口上传代码,则会出现 code_exist=0的情况。 | |||||
| */ | |||||
| @SerializedName("code_exist") | |||||
| private Integer codeExist; | |||||
| /** | |||||
| * 代码检测出来的用户信息类型(privacy_key) | |||||
| */ | |||||
| @SerializedName("privacy_list") | |||||
| private List<String> privacyList; | |||||
| /** | |||||
| * 要收集的用户信息配置 | |||||
| */ | |||||
| @SerializedName("setting_list") | |||||
| private List<Setting> settingList; | |||||
| /** | |||||
| * 更新时间 | |||||
| */ | |||||
| @SerializedName("update_time") | |||||
| private Long updateTime; | |||||
| /** | |||||
| * 收集方(开发者)信息配置 | |||||
| */ | |||||
| @SerializedName("owner_setting") | |||||
| private PrivacyOwnerSetting ownerSetting; | |||||
| /** | |||||
| * 收集方(开发者)信息配置 | |||||
| */ | |||||
| @SerializedName("privacy_desc") | |||||
| private PrivacyDesc privacyDesc; | |||||
| @Data | |||||
| public static class Setting { | |||||
| /** | |||||
| * 官方的可选值参考下方说明;该字段也支持自定义 | |||||
| * | |||||
| * @see PrivacyKeyEnum | |||||
| * @see PrivacyKeyEnum#getKey() | |||||
| */ | |||||
| @SerializedName("privacy_key") | |||||
| private String privacyKey; | |||||
| /** | |||||
| * 请填写收集该信息的用途。例如privacy_key=Location(位置信息),那么privacy_text则填写收集位置信息的用途。 | |||||
| * 无需再带上“为了”或者“用于”这些字眼,小程序端的显示格式是为了xxx,因此开发者只需要直接填写用途即可。 | |||||
| */ | |||||
| @SerializedName("privacy_text") | |||||
| private String privacyText; | |||||
| /** | |||||
| * 用户信息类型的中文名称 | |||||
| * | |||||
| * @see PrivacyKeyEnum#getDesc() () | |||||
| */ | |||||
| @SerializedName("privacy_label") | |||||
| private String privacyLabel; | |||||
| } | |||||
| @Data | |||||
| public static class PrivacyDesc { | |||||
| /** | |||||
| * 用户信息类型 | |||||
| */ | |||||
| @SerializedName("privacy_desc_list") | |||||
| private List<PrivacyDescItem> privacyDescList; | |||||
| } | |||||
| @Data | |||||
| public static class PrivacyDescItem { | |||||
| /** | |||||
| * 用户信息类型的英文key | |||||
| * | |||||
| * @see PrivacyKeyEnum | |||||
| * @see PrivacyKeyEnum#getKey() | |||||
| */ | |||||
| @SerializedName("privacy_key") | |||||
| private String privacyKey; | |||||
| /** | |||||
| * 用户信息类型的中文描述 | |||||
| * | |||||
| * @see PrivacyKeyEnum#getDesc() | |||||
| */ | |||||
| @SerializedName("privacy_desc") | |||||
| private String privacyDesc; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,62 @@ | |||||
| package me.chanjar.weixin.open.bean.ma.privacy; | |||||
| import lombok.AllArgsConstructor; | |||||
| import lombok.Getter; | |||||
| /** | |||||
| * 隐私key枚举 | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| @Getter | |||||
| @AllArgsConstructor | |||||
| public enum PrivacyKeyEnum { | |||||
| USER_INFO("UserInfo", "用户信息(微信昵称、头像)"), | |||||
| LOCATION("Location", "位置信息"), | |||||
| ADDRESS("Address", "地址"), | |||||
| INVOICE("Invoice", "发票信息"), | |||||
| RUN_DATA("RunData", "微信运动数据"), | |||||
| RECORD("Record", "麦克风"), | |||||
| ALBUM("Album", "选中的照片或视频信息"), | |||||
| CAMERA("Camera", "摄像头"), | |||||
| PHONE_NUMBER("PhoneNumber", "手机号码"), | |||||
| CONTACT("Contact", "通讯录(仅写入)权限"), | |||||
| DEVICE_INFO("DeviceInfo", "设备信息"), | |||||
| EXID_NUMBER("EXIDNumber", "身份证号码"), | |||||
| EX_ORDER_INFO("EXOrderInfo", "订单信息"), | |||||
| EX_USER_PUBLISH_CONTENT("EXUserPublishContent", "发布内容"), | |||||
| EX_USER_FOLLOW_ACCT("EXUserFollowAcct", "所关注账号"), | |||||
| EX_USER_OP_LOG("EXUserOpLog", "操作日志"), | |||||
| ALBUM_WRITE_ONLY("AlbumWriteOnly", "相册(仅写入)权限"), | |||||
| LICENSE_PLATE("LicensePlate", "车牌号"), | |||||
| BLUE_TOOTH("BlueTooth", "蓝牙"), | |||||
| CALENDAR_WRITE_ONLY("CalendarWriteOnly", "日历(仅写入)权限"), | |||||
| EMAIL("Email", "邮箱"), | |||||
| MESSAGE_FILE("MessageFile", "选中的文件"), | |||||
| ; | |||||
| private final String key; | |||||
| private final String desc; | |||||
| } | |||||
| @@ -0,0 +1,65 @@ | |||||
| package me.chanjar.weixin.open.bean.ma.privacy; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import lombok.AllArgsConstructor; | |||||
| import lombok.Builder; | |||||
| import lombok.Data; | |||||
| import lombok.NoArgsConstructor; | |||||
| import org.jetbrains.annotations.NotNull; | |||||
| /** | |||||
| * 小程序用户隐私保护指引 收集方(开发者)信息配置 | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| @Data | |||||
| @Builder | |||||
| @NoArgsConstructor | |||||
| @AllArgsConstructor | |||||
| public class PrivacyOwnerSetting { | |||||
| /** | |||||
| * 信息收集方(开发者)的邮箱地址,4种联系方式至少要填一种 | |||||
| */ | |||||
| @SerializedName("contact_email") | |||||
| private String contactEmail; | |||||
| /** | |||||
| * 信息收集方(开发者)的手机号,4种联系方式至少要填一种 | |||||
| */ | |||||
| @SerializedName("contact_phone") | |||||
| private String contactPhone; | |||||
| /** | |||||
| * 信息收集方(开发者)的qq号,4种联系方式至少要填一种 | |||||
| */ | |||||
| @SerializedName("contact_qq") | |||||
| private String contactQq; | |||||
| /** | |||||
| * 信息收集方(开发者)的微信号,4种联系方式至少要填一种 | |||||
| */ | |||||
| @SerializedName("contact_weixin") | |||||
| private String contactWeixin; | |||||
| /** | |||||
| * 如果开发者不使用微信提供的标准化用户隐私保护指引模板,也可以上传自定义的用户隐私保护指引,通过上传接口上传后可获取media_id | |||||
| */ | |||||
| @SerializedName("ext_file_media_id") | |||||
| private String extFileMediaId; | |||||
| /** | |||||
| * 通知方式,指的是当开发者收集信息有变动时,通过该方式通知用户。这里服务商需要按照实际情况填写,例如通过弹窗或者公告或者其他方式。 | |||||
| */ | |||||
| @NotNull | |||||
| @SerializedName("notice_method") | |||||
| private String noticeMethod; | |||||
| /** | |||||
| * 存储期限,指的是开发者收集用户信息存储多久。如果不填则展示为【开发者承诺,除法律法规另有规定,开发者对你的信息保存期限应当为实现处理目的所必要的最短时间】, | |||||
| * 如果填请填数字+天,例如“30天”,否则会出现87072的报错。 | |||||
| */ | |||||
| @SerializedName("store_expire_timestamp") | |||||
| private String storeExpireTimestamp; | |||||
| } | |||||
| @@ -0,0 +1,68 @@ | |||||
| package me.chanjar.weixin.open.bean.ma.privacy; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import lombok.AllArgsConstructor; | |||||
| import lombok.Builder; | |||||
| import lombok.Data; | |||||
| import lombok.NoArgsConstructor; | |||||
| import org.jetbrains.annotations.NotNull; | |||||
| import java.util.List; | |||||
| /** | |||||
| * 设置小程序用户隐私保护指引参数 | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| @Data | |||||
| @Builder | |||||
| @NoArgsConstructor | |||||
| @AllArgsConstructor | |||||
| public class SetPrivacySetting { | |||||
| /** | |||||
| * 用户隐私保护指引的版本,1表示现网版本;2表示开发版。默认是2开发版。 | |||||
| */ | |||||
| @SerializedName("privacy_ver") | |||||
| private Integer privacyVer; | |||||
| /** | |||||
| * 收集方(开发者)信息配置 | |||||
| */ | |||||
| @NotNull | |||||
| @SerializedName("owner_setting") | |||||
| private PrivacyOwnerSetting ownerSetting; | |||||
| /** | |||||
| * 要收集的用户信息配置 | |||||
| */ | |||||
| @NotNull | |||||
| @SerializedName("setting_list") | |||||
| private List<Setting> settingList; | |||||
| @Data | |||||
| @Builder | |||||
| @NoArgsConstructor | |||||
| @AllArgsConstructor | |||||
| public static class Setting { | |||||
| /** | |||||
| * 官方的可选值参考下方说明;该字段也支持自定义 | |||||
| * | |||||
| * @see PrivacyKeyEnum | |||||
| * @see PrivacyKeyEnum#getKey() | |||||
| */ | |||||
| @NotNull | |||||
| @SerializedName("privacy_key") | |||||
| private String privacyKey; | |||||
| /** | |||||
| * 请填写收集该信息的用途。例如privacy_key=Location(位置信息),那么privacy_text则填写收集位置信息的用途。 | |||||
| * 无需再带上“为了”或者“用于”这些字眼,小程序端的显示格式是为了xxx,因此开发者只需要直接填写用途即可。 | |||||
| */ | |||||
| @NotNull | |||||
| @SerializedName("privacy_text") | |||||
| private String privacyText; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| package me.chanjar.weixin.open.bean.ma.privacy; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import lombok.Getter; | |||||
| import lombok.Setter; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenResult; | |||||
| /** | |||||
| * 上传小程序用户隐私保护指引文件 响应 | |||||
| * | |||||
| * @author <a href="https://www.sacoc.cn">广州跨界</a> | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public class UploadPrivacyFileResult extends WxOpenResult { | |||||
| /** | |||||
| * 文件的media_id | |||||
| */ | |||||
| @SerializedName("ext_file_media_id") | |||||
| private String extFileMediaId; | |||||
| } | |||||