精华内容
下载资源
问答
  • java后台向app推送消息

    2016-06-20 09:56:46
    java后台怎么向app推送消息呢,需要用到哪些技术 ?公司现在是做美容服务预约软件,有一个客户端app和一个技师端app,后台专门写了一个客户端提交订单的接口,客户提交服务订单以后后台接收订单信息,然后向预定的...
  • php 给APP推送消息

    千次阅读 2017-06-27 09:23:57
    php 给APP推送消息有很多, 极光,信鸽,百度等。 刚开始用的时候 是 极光,因为是 thinkphp 3.2 不能用composer ,所以出现各种坑 。一直提示找不到CLASS,果断弃坑 (很大可能是命名空间的问题 ,这方面 投入...

    php 给APP推送消息有很多, 极光,信鸽,百度等。


    刚开始用的时候 是 极光,因为是 thinkphp 3.2   不能用composer  ,所以出现各种坑 。一直提示找不到CLASS,果断弃坑 (很大可能是命名空间的问题 ,这方面 投入时间少,熟练度不够)。

    然后用百度推送,五分钟OK,  方法和极光一样  new  一下,就可以用了,但是测试有个问题,准点率太低了,好在 能用,只需要获取手机设备号。  领导说腾讯有推送 。

    接着看腾讯 ,也就是信鸽推送方法也和上面差不多,但是必须安卓配合,给我传token,然后直接用token发送。  这里我的需求是预约发送,一次多个用户 。


    现在做好了, 总结一下问题  。

    首先  谈谈 安卓 方面 遇到的一个 问题。我们 安卓刚开始的时候 为了防止反编译, 包名不是真实的,所以 友盟推送 不能用(友盟必须真实包名)

    第一次用的极光推送,一直没有成功。现在看来 原因就在于 命名空间 绝对有问题 。

    最后我用的是信鸽推送  也就是腾讯推送。php 来说 很简单,关键就是 命名空间  和调用变量的写法 ,千万不能错, 出现一丝 问题 ,就会提醒   找不到对应的class.

    展开全文
  • python 全栈开发,Day131(向app推送消息,玩具端消息推送) 先下载github代码,下面的操作,都是基于这个版本来的! https://github.com/987334176/Intelligent_toy/archive/v1.4.zip 注意:由于...

    python 全栈开发,Day131(向app推送消息,玩具端消息推送)

    先下载github代码,下面的操作,都是基于这个版本来的!

    https://github.com/987334176/Intelligent_toy/archive/v1.4.zip

    注意:由于涉及到版权问题,此附件没有图片和音乐。请参考链接,手动采集一下!

    请参考链接:

    https://www.cnblogs.com/xiao987334176/p/9647993.html#autoid-3-4-0

     

    一、向app推送消息

    redis安装

    Redis项目并没有正式支持Windows。然而,微软OpenTeaGeo集团开发并维护了以WIN64为目标的Windows端口。链接如下:

    https://github.com/MicrosoftArchive/redis/releases

     

    目前最新版本是3.2

     

    完整下载链接如下:

    https://github.com/MicrosoftArchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.msi

     

    开始安装

     注意:要勾选添加到系统环境变量

     默认端口是6379

    设置最大内存,这里不设置。表示不限制内存大小!

    安装完成后,打开cmd窗口,运行命令

    d:
    
    cd D:\Program Files\Redis
    
    redis-server.exe redis.windows.conf
    
    redis-cli

    效果如下:

     

    使用Python操作redis,需要安装模块redis

    pip install redis

    接下来的操作,会将消息数量,存在redis中!

     

    显示消息数量

    如果有消息来了,需要在底部选项卡中的消息按钮,显示角标。比如这样:

    还有 好友列表,也要显示角标。谁发送了几条消息,比如这样:

     

    消息按钮显示未读消息

    进入flask项目,修改setting.py,增加redis配置

    import pymongo
    import os
    import redis
    
    # 数据库配置
    client = pymongo.MongoClient(host="127.0.0.1", port=27017)
    MONGO_DB = client["bananabase"]
    
    REDIS_DB = redis.Redis(host="127.0.0.1",port=6379)
    
    RET = {
        # 0: false 2: True
        "code": 0,
        "msg": "",  # 提示信息
        "data": {}
    }
    
    XMLY_URL = "http://m.ximalaya.com/tracks/"  # 喜马拉雅链接
    CREATE_QR_URL = "http://qr.liantu.com/api.php?text="  # 生成二维码API
    
    # 文件目录
    
    
    AUDIO_FILE = os.path.join(os.path.dirname(__file__), "audio")  # 音频
    AUDIO_IMG_FILE = os.path.join(os.path.dirname(__file__), "audio_img")  # 音频图片
    
    DEVICE_CODE_PATH = os.path.join(os.path.dirname(__file__), "device_code")  # 二维码
    CHAT_FILE = os.path.join(os.path.dirname(__file__), "chat")  # 聊天
    
    # 百度AI配置
    APP_ID = "117912345"
    API_KEY = "3v3igzCkVFUDwFByNEE12345"
    SECRET_KEY = "jRnwLE7kzC1aRi2FD10OQY3y9O12345"
    SPEECH = {
        "spd": 4,
        'vol': 5,
        "pit": 8,
        "per": 4
    }
    View Code

     

    进入utils文件夹,创建文件 chat_redis.py

    from setting import REDIS_DB
    import json
    
    
    def save_msg(小甜甜,xiao):
        """
    
        key: xiao
        {
            小甜甜:5,
            小豆芽:4
        }
        :return:
    
        """
        res = json.loads(REDIS_DB.get(xiao))
        res["小甜甜"] = 1
        if res:
            pass
    
        REDIS_DB.set(xiao,json.dumps(res))
    View Code

     

    看下面的示例图:

     

    修改 chat_redis.py

    from setting import REDIS_DB
    import json
    
    
    def save_msg(sender, to_user):  # 保存消息
        # 1.查询一下xiao的Redis是否有数据
        user_msg_redis = REDIS_DB.get(to_user)
        if user_msg_redis:
            # 2.将xiao的数据反序列化成字典 { sender : n }
            user_msg_dict = json.loads(user_msg_redis)
            # 3.判断有没有 sender 的用户发来的消息数量
            if user_msg_dict.get(sender):
                # 数量加1
                user_msg_dict[sender] += 1
            else:
                # 第一次,初始值为1
                user_msg_dict[sender] = 1
        # 4.如果xiao是刚建立好的用户,他是没有消息的,字典是空
        else:
            user_msg_dict = {sender: 1}
    
        # 5.序列化用户消息字典user_msg_dict
        user_msg_redis = json.dumps(user_msg_dict)
        # 6.存回Redis
        REDIS_DB.set(to_user, user_msg_redis)
    
    def get_msg_list(user):  # 获取消息
        user_msg_redis = REDIS_DB.get(user)
        if user_msg_redis:
            user_msg_dict = json.loads(user_msg_redis)
            # 统计数量
            user_msg_dict["count"] = sum(user_msg_dict.values())
        else:
            user_msg_dict = {"count":0}
    
        return user_msg_dict
    
    def get_user_msg_one(sender, to_user):  # 获取一条消息
        user_msg_redis = REDIS_DB.get(to_user)
        if user_msg_redis:
            user_msg_dict = json.loads(user_msg_redis)
            if user_msg_dict.get(sender):
                return user_msg_dict.get(sender)
    View Code

     

    修改 im_serv.py

    from flask import Flask, request
    from geventwebsocket.websocket import WebSocket
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    import json, os
    from uuid import uuid4
    from setting import AUDIO_FILE,CHAT_FILE
    from serv import content
    from utils import baidu_ai
    from utils import chat_redis
    import setting
    from bson import ObjectId
    import time
    
    app = Flask(__name__)
    
    user_socket_dict = {}  # 空字典,用来存放用户名和发送消息
    
    
    @app.route("/toy/<tid>")
    def toy(tid):  # 玩具连接
        # 获取请求的WebSocket对象
        user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
        if user_socket:
            # 设置键值对
            user_socket_dict[tid] = user_socket
            print(user_socket_dict)
            # {'123456': <geventwebsocket.websocket.WebSocket object at 0x00000176ABD92E18>}
    
        file_name = ""
        to_user = ""
        # 循环,接收消息
        while True:
            msg = user_socket.receive()
            if type(msg) == bytearray:
                file_name = f"{uuid4()}.wav"
                file_path = os.path.join(CHAT_FILE, file_name)
                with open(file_path, "wb") as f:
                    f.write(msg)
            else:
                msg_dict = json.loads(msg)
                to_user = msg_dict.get("to_user")
                msg_type = msg_dict.get("msg_type")
    
            if to_user and file_name:
                other_user_socket = user_socket_dict.get(to_user)
                if msg_type == "ai":
                    q = baidu_ai.audio2text(file_path)
                    print(q)
                    ret = baidu_ai.my_nlp(q, tid)
                    other_user_socket.send(json.dumps(ret))
                else:
                    send_str = {
                        "code": 0,
                        "from_user": tid,
                        "msg_type": "chat",
                        "data": file_name
                    }
    
                    if other_user_socket:  # 当websocket连接存在时
                        chat_redis.save_msg(tid, to_user)  # 保存消息到redis
                        # 发送数据
                        other_user_socket.send(json.dumps(send_str))
                    else:
                        # 离线消息
                        chat_redis.save_msg(tid, to_user)
    
                    # 保存聊天记录到MongoDB
                    _add_chat(tid, to_user, send_str.get("data"))
    
                to_user = ""
                file_name = ""
    
    
    @app.route("/app/<uid>")
    def user_app(uid):  # 手机app连接
        user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
        if user_socket:
            user_socket_dict[uid] = user_socket
            # { uid : websocket}
            print(user_socket_dict)
    
        file_name = ""
        to_user = ""
    
        while True:  # 手机听歌 把歌曲发送给 玩具 1.将文件直接发送给玩具 2.将当前听的歌曲名称或ID发送到玩具
            msg = user_socket.receive()
            if type(msg) == bytearray:  # 判断类型为bytearray
                file_name = f"{uuid4()}.amr"  # 文件后缀为amr,安卓和ios通用
                file_path = os.path.join(CHAT_FILE, file_name)  # 存放在chat目录
                print(msg)
                with open(file_path, "wb") as f:
                    f.write(msg)  # 写入文件
    
                # 将amr转换为mp3,因为html中的audio不支持amr
                os.system(f"ffmpeg -i {file_path} {file_path}.mp3")
    
            else:
                msg_dict = json.loads(msg)
                to_user = msg_dict.get("to_user")  # 获取目标用户
    
                if msg_dict.get("msg_type") == "music":
                    other_user_socket = user_socket_dict.get(to_user)
    
                    send_str = {
                        "code": 0,
                        "from_user": uid,
                        "msg_type": "music",
                        "data": msg_dict.get("data")
                    }
                    other_user_socket.send(json.dumps(send_str))
    
                # res = content._content_one(content_id)
            if file_name and to_user:  # 如果文件名和发送用户同上存在时
                # 查询玩具信息
                res = setting.MONGO_DB.toys.find_one({"_id": ObjectId(to_user)})
                # 获取friend_remark
                fri = [i.get("friend_remark") for i in res.get("friend_list") if i.get("friend_id") == uid][0]
                msg_file_name = baidu_ai.text2audio(f"你有来自{fri}的消息")
    
                # 获取websocket对象
                other_user_socket = user_socket_dict.get(to_user)
                # 构造数据
                send_str = {
                    "code": 0,
                    "from_user": uid,
                    "msg_type": "chat", # 聊天类型
                    # 后缀必须是mp3的
                    "data": msg_file_name
                }
                # 发送数据给前端页面
                other_user_socket.send(json.dumps(send_str))
                # 添加聊天记录到数据库
                _add_chat(uid, to_user, f"{file_name}.mp3")
                # 最后一定要清空这2个变量,否则造成混乱
                file_name = ""
                to_user = ""
    
    def _add_chat(sender, to_user, msg):  # 添加聊天记录到数据库
        chat_window = setting.MONGO_DB.chat.find_one({"user_list": {"$all": [sender, to_user]}})
        if not chat_window.get("chat_list"):
            chat_window["chat_list"] = [{
                "sender": sender,
                "msg": msg,
                "updated_at": time.time(),
            }]
            res = setting.MONGO_DB.chat.update_one({"_id": ObjectId(chat_window.get("_id"))}, {"$set": chat_window})
        else:
            chat = {
                "sender": sender,
                "msg": msg,
                "updated_at": time.time(),
            }
            res = setting.MONGO_DB.chat.update_one({"_id": ObjectId(chat_window.get("_id"))}, {"$push": {"chat_list": chat}})
    
        return res
    
    if __name__ == '__main__':
        # 创建一个WebSocket服务器
        http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler)
        # 开始监听HTTP请求
        http_serv.serve_forever()
    
    
    '''
    {
        "code": 0,
        "from_user": uid,  # APP用户id
        "data": music_name  # 歌曲名
    }
    '''
    View Code

    重启  im_serv.py

     

    打开网页,让 小甜甜 开机

     

    点击 开始废话,使用麦克风说: 给小鱼 发消息。再点击发送语音!

    此时,录制消息按钮后面,会出现 对方id

    注意:小鱼 表示玩具对主人(xiao)的称呼

     

    再点击 录制消息,说: hello。最后点击发送语音消息

     

    进入redis,查看消息,将网页中的 录制消息按钮后面的id复制过来

     

    查看用户id的数据,可以发现数量为1

    127.0.0.1:6379> get 5b9bb768e1253281608e96eb
    "{\"5ba0f1f2e12532418089bf88\": 1}"
    127.0.0.1:6379>

    那么redis中的数据有了,就可以在APP中渲染了

     

    进入HBuilder项目MyApp,修改 index.html,增加角标

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <title></title>
            <script src="js/mui.js"></script>
            <link href="css/mui.min.css" rel="stylesheet" />
        </head>
    
        <body>
            <!--底部选项卡-->
            <nav class="mui-bar mui-bar-tab">
                <a class="mui-tab-item mui-active" id="index">
                    <span class="mui-icon mui-icon-home"></span>
                    <span class="mui-tab-label">首页</span>
                </a>
                <a class="mui-tab-item" id="message">
                    <span class="mui-icon mui-icon-chat">
                        <span class="mui-badge mui-badge-red" id="msg_count">0</span>
                    </span>
                    <span class="mui-tab-label">消息</span>
                </a>
                <a class="mui-tab-item">
                    <span class="mui-icon mui-icon-email"></span>
                    <span class="mui-tab-label">邮件</span>
                </a>
                <a class="mui-tab-item" id="login">
                    <span class="mui-icon mui-icon-gear"></span>
                    <span class="mui-tab-label">设置</span>
                </a>
            </nav>
        </body>
        <script type="text/javascript" charset="utf-8">
            var ws = null; // websocket对象
            mui.init({
                subpages: [{
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                }]
            });
            mui.plusReady(function() {
                //            console.log(JSON.stringify(plus.webview.currentWebview()))
                if(plus.storage.getItem("user")) { // 判断是否登录
                    console.log('已结登录了!');
                    //连接websocket连接
                    ws = new WebSocket("ws://" + window.ws_serv + "/app/" + plus.storage.getItem("user"))
                    
                    // 发送post请求
                    console.log(window.serv + "/get_msg_list");
                    mui.post(
                        // 访问消息列表
                        window.serv + "/get_msg_list", {
                            user_id: plus.storage.getItem("user")
                        },
                        function(data) {
                            console.log(JSON.stringify(data));
                            msg_data = data.data;
                            // 修改消息选项卡的角标数字
                            document.getElementById("msg_count").innerText = msg_data.count;
                        }
                    );
                    
                    // 客户端接收服务端数据时触发
                    ws.onmessage = function() {};
                }
                // 自动重连
                ws.onclose = function() {
                    window.location.reload();
                }
            });
    
            // 消息
            document.getElementById("message").addEventListener("tap", function() {
                mui.openWindow({
                    url: "message.html",
                    id: "message.html",
                    styles: window.styles,
                    extras: {
                        // 传输用户id,给message.html
                        user_id: plus.storage.getItem("user")
                    }
                })
            });
    
            document.getElementById("index").addEventListener("tap", function() {
                mui.openWindow({
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                })
            })
    
            document.getElementById("login").addEventListener("tap", function() {
                // 自动登录,判断storage中的user存在,就跳转到user_info,否则跳转login
                if(plus.storage.getItem("user")) {
                    mui.openWindow({
                        url: "user_info.html",
                        id: "user_info.html",
                        styles: window.styles,
                        extras: {
                            user_id: plus.storage.getItem("user")
                        }
                    })
                } else {
                    mui.openWindow({
                        url: "login.html",
                        id: "login.html",
                        styles: window.styles
                    })
                }
            })
    
            document.addEventListener("login", function(data) {
                // fire事件接收消息,使用data.detail
                // index是为做显示区分
                mui.toast("index" + data.detail.msg)
            });
    
            document.addEventListener("send_music", function(data) { //监听send_music事件
                var music_name = data.detail.music_name; //获取player.html使用fire发送的music_name值
                var toy_id = data.detail.toy_id; //获取发送的玩具id
    
                send_str = { //构造数据
                    data: music_name,
                    to_user: toy_id, // 目标用户,这里统一格式
                    msg_type: "music", // 类型为音乐
                }
                // 发送数据给后端,注意要json序列化
                ws.send(JSON.stringify(send_str));
            });
    
            document.addEventListener("send_msg", function(data) { //发送消息
                var filename = data.detail.filename
                var to_user = data.detail.to_user
                send_str = {
                    to_user: to_user
                }
                ws.send(JSON.stringify(send_str))
                plus.io.resolveLocalFileSystemURL(filename, function(entry) {
                    // 可通过entry对象操作test.html文件 
                    entry.file(function(file) {
                        // FileReader文件系统中的读取文件对象,用于获取文件的内容
                        var fileReader = new plus.io.FileReader();
    
                        //                        alert("getFile:" + JSON.stringify(file));
                        // readAsDataURL: 以URL编码格式读取文件数据内容
                        fileReader.readAsDataURL(file, 'utf-8');
                        // onloadend: 文件读取操作完成时的回调函数
                        fileReader.onloadend = function(evt) {
                            console.log(evt.target.result);
                            var b = dataURLtoBlob(evt.target.result);
                            ws.send(b); // 发送blob数据
    
                        }
                        //                        alert(file.size + '--' + file.name)
                    });
                });
    
            })
    
            function dataURLtoBlob(dataurl) { // 数据转换为Blob
                // 逻辑很复杂,这里不解释了。直接用就可以了!
                var arr = dataurl.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);
                while(n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                var a = new Blob([u8arr], {
                    type: mime
                });
                return a
            }
        </script>
    
    </html>
    View Code

     

    进入 flask项目,修改 serv-->chat.py

    from flask import Blueprint, request, jsonify
    from setting import MONGO_DB
    from setting import RET
    from bson import ObjectId
    from utils import chat_redis
    
    cht = Blueprint("cht", __name__)
    
    
    @cht.route("/chat_list", methods=["POST"])
    def chat_list():  # 聊天记录列表
        user_id = request.form.get("user_id")
        friend_id = request.form.get("friend_id")
        print(friend_id)
    
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, friend_id]}})
        fri = MONGO_DB.toys.find_one({"_id": ObjectId(friend_id)})
        baby_name = fri.get("baby_name")
        cl = chat_window.get("chat_list")
    
        RET["code"] = 0
        RET["msg"] = baby_name
        RET["data"] = cl
    
        return jsonify(RET)
    
    
    @cht.route("/get_msg", methods=["POST"])
    def get_msg():  # 获取聊天语言文件
        user_id = request.form.get("user_id")
        sender = request.form.get("sender")
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, sender]}})
        new_msg = chat_window.get("chat_list")[-1]
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = new_msg.get("msg")
    
        return jsonify(RET)
    
    @cht.route("/get_msg_list", methods=["POST"])
    def get_msg_list():
        user_id = request.form.get("user_id")
        user_msg_dict = chat_redis.get_msg_list(user_id)
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = user_msg_dict
    
        return jsonify(RET)
    View Code

    重启 manager.py

     

    打开模拟器,关闭里面的HBuilder进程,重新开启,效果如下:

    效果就完成了!

     

    聊天窗口显示未读消息

    再来做 聊天窗口,显示角标。上面演示时,小甜甜 给 小鱼(主人) 发送了一条消息。

    那么下图中的紫色框后面,应该显示数字1

    那么如何实现呢?答案很简单,使用mui.openWindows打开message.html页面时,给它传一个参数msg_data。

    参数大概是这个样子

    "data":{"5ba0f1f2e12532418089bf88":1,"count":1}

    5ba0f1f2e12532418089bf88 是 小甜甜 的_id。在toys表中可以查询!

     

    修改 index.html

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <title></title>
            <script src="js/mui.js"></script>
            <link href="css/mui.min.css" rel="stylesheet" />
        </head>
    
        <body>
            <!--底部选项卡-->
            <nav class="mui-bar mui-bar-tab">
                <a class="mui-tab-item mui-active" id="index">
                    <span class="mui-icon mui-icon-home"></span>
                    <span class="mui-tab-label">首页</span>
                </a>
                <a class="mui-tab-item" id="message">
                    <span class="mui-icon mui-icon-chat">
                        <span class="mui-badge mui-badge-red" id="msg_count">0</span>
                    </span>
                    <span class="mui-tab-label">消息</span>
                </a>
                <a class="mui-tab-item">
                    <span class="mui-icon mui-icon-email"></span>
                    <span class="mui-tab-label">邮件</span>
                </a>
                <a class="mui-tab-item" id="login">
                    <span class="mui-icon mui-icon-gear"></span>
                    <span class="mui-tab-label">设置</span>
                </a>
            </nav>
        </body>
        <script type="text/javascript" charset="utf-8">
            var ws = null; // websocket对象
            var msg_data = null;  // 消息数据
            mui.init({
                subpages: [{
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                }]
            });
            mui.plusReady(function() {
                //            console.log(JSON.stringify(plus.webview.currentWebview()))
                if(plus.storage.getItem("user")) { // 判断是否登录
                    console.log('已结登录了!');
                    //连接websocket连接
                    ws = new WebSocket("ws://" + window.ws_serv + "/app/" + plus.storage.getItem("user"))
                    
                    // 发送post请求
                    console.log(window.serv + "/get_msg_list");
                    mui.post(
                        // 访问消息列表
                        window.serv + "/get_msg_list", {
                            user_id: plus.storage.getItem("user")
                        },
                        function(data) {
                            console.log(JSON.stringify(data));
                            //  {"code":0,"data":{"5ba0f1f2e12532418089bf88":1,"count":1},"msg":""}
                            msg_data = data.data;
                            // 修改消息选项卡的角标数字
                            document.getElementById("msg_count").innerText = msg_data.count;
                        }
                    );
                    
                    // 客户端接收服务端数据时触发
                    ws.onmessage = function() {};
                }
                // 自动重连
                ws.onclose = function() {
                    window.location.reload();
                }
            });
    
            // 消息
            document.getElementById("message").addEventListener("tap", function() {
                mui.openWindow({
                    url: "message.html",
                    id: "message.html",
                    styles: window.styles,
                    extras: {
                        // 传输用户id,给message.html
                        user_id: plus.storage.getItem("user"),
                        msg_data: msg_data,
                        //  "data":{"5ba0f1f2e12532418089bf88":1,"count":1}
                    }
                })
            });
    
            document.getElementById("index").addEventListener("tap", function() {
                mui.openWindow({
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                })
            })
    
            document.getElementById("login").addEventListener("tap", function() {
                // 自动登录,判断storage中的user存在,就跳转到user_info,否则跳转login
                if(plus.storage.getItem("user")) {
                    mui.openWindow({
                        url: "user_info.html",
                        id: "user_info.html",
                        styles: window.styles,
                        extras: {
                            user_id: plus.storage.getItem("user")
                        }
                    })
                } else {
                    mui.openWindow({
                        url: "login.html",
                        id: "login.html",
                        styles: window.styles
                    })
                }
            })
    
            document.addEventListener("login", function(data) {
                // fire事件接收消息,使用data.detail
                // index是为做显示区分
                mui.toast("index" + data.detail.msg)
            });
    
            document.addEventListener("send_music", function(data) { //监听send_music事件
                var music_name = data.detail.music_name; //获取player.html使用fire发送的music_name值
                var toy_id = data.detail.toy_id; //获取发送的玩具id
    
                send_str = { //构造数据
                    data: music_name,
                    to_user: toy_id, // 目标用户,这里统一格式
                    msg_type: "music", // 类型为音乐
                }
                // 发送数据给后端,注意要json序列化
                ws.send(JSON.stringify(send_str));
            });
    
            document.addEventListener("send_msg", function(data) { //发送消息
                var filename = data.detail.filename
                var to_user = data.detail.to_user
                send_str = {
                    to_user: to_user
                }
                ws.send(JSON.stringify(send_str))
                plus.io.resolveLocalFileSystemURL(filename, function(entry) {
                    // 可通过entry对象操作test.html文件 
                    entry.file(function(file) {
                        // FileReader文件系统中的读取文件对象,用于获取文件的内容
                        var fileReader = new plus.io.FileReader();
    
                        //                        alert("getFile:" + JSON.stringify(file));
                        // readAsDataURL: 以URL编码格式读取文件数据内容
                        fileReader.readAsDataURL(file, 'utf-8');
                        // onloadend: 文件读取操作完成时的回调函数
                        fileReader.onloadend = function(evt) {
                            console.log(evt.target.result);
                            var b = dataURLtoBlob(evt.target.result);
                            ws.send(b); // 发送blob数据
    
                        }
                        //                        alert(file.size + '--' + file.name)
                    });
                });
    
            })
    
            function dataURLtoBlob(dataurl) { // 数据转换为Blob
                // 逻辑很复杂,这里不解释了。直接用就可以了!
                var arr = dataurl.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);
                while(n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                var a = new Blob([u8arr], {
                    type: mime
                });
                return a
            }
        </script>
    
    </html>
    View Code

     

    修改 message.html,渲染页面。create_content多加一个参数

    <!doctype html>
    <html lang="en">
    
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <link rel="stylesheet" type="text/css" href="css/mui.css" />
        </head>
    
        <body>
            <header class="mui-bar mui-bar-nav">
                <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
                <h1 class="mui-title">我的好友</h1>
            </header>
            <div class="mui-content">
                <ul class="mui-table-view" id="friend_list">
    
                </ul>
            </div>
        </body>
        <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            mui.init()
            var Sdata = null;
            mui.back = function(){};
    
            // 加载HTML5Puls
            mui.plusReady(function() {
                Sdata = plus.webview.currentWebview();
                // post请求
                mui.post(
                    // 好友列表
                    window.serv + "/friend_list",
                    {user_id:Sdata.user_id},
                    function(data){
                        console.log(JSON.stringify(data));
                        // 循环好友列表
                        for (var i = 0; i < data.data.length; i++) {
                            // 执行自定义方法,渲染页面
                            create_content(data.data[i],Sdata.msg_data);
                        }
                    }
                )
            });
            
            function create_content(content,msg_data){    
    //            <li class="mui-table-view-cell mui-media">
    //                <a href="javascript:;">
    //                    <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg">
    //                    <div class="mui-media-body">
    //                        幸福
    //                        <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p>
    //                    </div>
    //                </a>
    //            </li>
                // 角标
                var spantag = document.createElement("span");
                spantag.className = "mui-badge mui-badge-red";
                // content是一个字典,要获取friend_id。不能使用get,只能使用.
                // 如果获取不到,值为undefine
                spantag.innerText = msg_data[content.friend_id]
                
                var litag = document.createElement("li");
                litag.className = "mui-table-view-cell mui-media";
                var atag = document.createElement("a");
                atag.id = content.friend_id;
                // 点击事件
                atag.onclick = function(){
                    console.log(this.id);
                    open_chat(this.id);  //执行自定义方法open_chat
                }
    
                var imgtag = document.createElement("img");
                imgtag.className = "mui-media-object mui-pull-left";
                
                imgtag.src = "avatar/" + content.friend_avatar;
                
                var divtag = document.createElement("div");
                divtag.className = "mui-media-body";
                divtag.innerText = content.friend_remark;
                var ptag = document.createElement("p");
                ptag.className = "mui-ellipsis";
                ptag.innerText = content.friend_name;
                 
                 litag.appendChild(atag);
                 atag.appendChild(imgtag);
                 atag.appendChild(divtag);
                 atag.appendChild(spantag);
                 divtag.appendChild(ptag);
                 
                 document.getElementById("friend_list").appendChild(litag);
            }
            
            function open_chat(friend_id){  // 打开chat.html
                mui.openWindow({
                    url:"chat.html",
                    id:"chat.html",
                    extras:{
                        // 传参给chat.html
                        friend_id:friend_id
                    }
                })
            }
    
        </script>
    
    </html>
    View Code

     

    使用模拟器重新访问,效果如下:

     发现了 undefined,这个不应该出现角标。这个是一个已知的bug,有兴趣的人,可以修改一下。

    提示:使用css中的display:none

     

    角标重置

    点击 小甜甜,再返回页面时,这里的角标应该重置为0。并且不显示才对。

    前端操作

    修改 message.html,触发点击事件时,角标重置为0

    <!doctype html>
    <html lang="en">
    
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <link rel="stylesheet" type="text/css" href="css/mui.css" />
        </head>
    
        <body>
            <header class="mui-bar mui-bar-nav">
                <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
                <h1 class="mui-title">我的好友</h1>
            </header>
            <div class="mui-content">
                <ul class="mui-table-view" id="friend_list">
    
                </ul>
            </div>
        </body>
        <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            mui.init()
            var Sdata = null;
            mui.back = function(){};
    
            // 加载HTML5Puls
            mui.plusReady(function() {
                Sdata = plus.webview.currentWebview();
                // post请求
                mui.post(
                    // 好友列表
                    window.serv + "/friend_list",
                    {user_id:Sdata.user_id},
                    function(data){
                        console.log(JSON.stringify(data));
                        // 循环好友列表
                        for (var i = 0; i < data.data.length; i++) {
                            // 执行自定义方法,渲染页面
                            create_content(data.data[i],Sdata.msg_data);
                        }
                    }
                )
            });
            
            function create_content(content,msg_data){    
    //            <li class="mui-table-view-cell mui-media">
    //                <a href="javascript:;">
    //                    <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg">
    //                    <div class="mui-media-body">
    //                        幸福
    //                        <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p>
    //                    </div>
    //                </a>
    //            </li>
                // 角标
                var spantag = document.createElement("span");
                spantag.className = "mui-badge mui-badge-red";
                // content是一个字典,要获取friend_id。不能使用get,只能使用.
                // 如果获取不到,值为undefine
                spantag.innerText = msg_data[content.friend_id]
                
                var litag = document.createElement("li");
                litag.className = "mui-table-view-cell mui-media";
                var atag = document.createElement("a");
                atag.id = content.friend_id;
                // 点击事件
                atag.onclick = function(){
                    console.log(this.id);
                    spantag.innerText = 0;  // 重置为0
                    //执行自定义方法open_chat
                    open_chat(this.id);
                    
                }
    
                var imgtag = document.createElement("img");
                imgtag.className = "mui-media-object mui-pull-left";
                
                imgtag.src = "avatar/" + content.friend_avatar;
                
                var divtag = document.createElement("div");
                divtag.className = "mui-media-body";
                divtag.innerText = content.friend_remark;
                var ptag = document.createElement("p");
                ptag.className = "mui-ellipsis";
                ptag.innerText = content.friend_name;
                 
                 litag.appendChild(atag);
                 atag.appendChild(imgtag);
                 atag.appendChild(divtag);
                 atag.appendChild(spantag);
                 divtag.appendChild(ptag);
                 
                 document.getElementById("friend_list").appendChild(litag);
            }
            
            function open_chat(friend_id){  // 打开chat.html
                mui.openWindow({
                    url:"chat.html",
                    id:"chat.html",
                    extras:{
                        // 传参给chat.html
                        friend_id:friend_id
                    }
                })
            }
    
        </script>
    
    </html>
    View Code

     

    使用模拟器访问,效果如下:

    可以发现,聊天列表返回时,已经重置为0了。但是底部现象卡还没有变动!莫急,下面来处理它。

    怎么实现呢?由于index.html页面是母模,它只负责显示底部选项卡。

    在chat.html页面给index.html,执行一个fire(开火)事件就可以了!但是:一个页面,只能fire一次。

    由于chat.html已经存在了一个fire事件。所以只能在message.html做fire

     

    修改 message.html,增加fire

    <!doctype html>
    <html lang="en">
    
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <link rel="stylesheet" type="text/css" href="css/mui.css" />
        </head>
    
        <body>
            <header class="mui-bar mui-bar-nav">
                <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
                <h1 class="mui-title">我的好友</h1>
            </header>
            <div class="mui-content">
                <ul class="mui-table-view" id="friend_list">
    
                </ul>
            </div>
        </body>
        <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            mui.init()
            var Sdata = null;
            mui.back = function(){};
    
            // 加载HTML5Puls
            mui.plusReady(function() {
                Sdata = plus.webview.currentWebview();
                // post请求
                mui.post(
                    // 好友列表
                    window.serv + "/friend_list",
                    {user_id:Sdata.user_id},
                    function(data){
                        console.log(JSON.stringify(data));
                        // 循环好友列表
                        for (var i = 0; i < data.data.length; i++) {
                            // 执行自定义方法,渲染页面
                            create_content(data.data[i],Sdata.msg_data);
                        }
                    }
                )
            });
            
            function create_content(content,msg_data){    
    //            <li class="mui-table-view-cell mui-media">
    //                <a href="javascript:;">
    //                    <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg">
    //                    <div class="mui-media-body">
    //                        幸福
    //                        <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p>
    //                    </div>
    //                </a>
    //            </li>
                // 角标
                var spantag = document.createElement("span");
                spantag.className = "mui-badge mui-badge-red";
                // content是一个字典,要获取friend_id。不能使用get,只能使用.
                // 如果获取不到,值为undefine
                spantag.innerText = msg_data[content.friend_id]
                
                var litag = document.createElement("li");
                litag.className = "mui-table-view-cell mui-media";
                var atag = document.createElement("a");
                atag.id = content.friend_id;
                // 点击事件
                atag.onclick = function(){
    //                console.log(this.id);
                    //执行自定义方法open_chat
                    open_chat(this.id,spantag.innerText);
                    spantag.innerText = 0;  // 重置为0
                    
                    
                }
    
                var imgtag = document.createElement("img");
                imgtag.className = "mui-media-object mui-pull-left";
                
                imgtag.src = "avatar/" + content.friend_avatar;
                
                var divtag = document.createElement("div");
                divtag.className = "mui-media-body";
                divtag.innerText = content.friend_remark;
                var ptag = document.createElement("p");
                ptag.className = "mui-ellipsis";
                ptag.innerText = content.friend_name;
                 
                 litag.appendChild(atag);
                 atag.appendChild(imgtag);
                 atag.appendChild(divtag);
                 atag.appendChild(spantag);
                 divtag.appendChild(ptag);
                 
                 document.getElementById("friend_list").appendChild(litag);
            }
            
            function open_chat(friend_id,cut_count){  // 打开chat.html
                // 获取index.html
                var index = plus.webview.getWebviewById("HBuilder")
                // 执行fire
                mui.fire(index,"cut_msg_count",{cut:cut_count})
                mui.openWindow({
                    url:"chat.html",
                    id:"chat.html",
                    extras:{
                        // 传参给chat.html
                        friend_id:friend_id
                    }
                })
            }
    
        </script>
    
    </html>
    View Code

     

    修改 index.html,监听 cut_msg_count事件

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <title></title>
            <script src="js/mui.js"></script>
            <link href="css/mui.min.css" rel="stylesheet" />
        </head>
    
        <body>
            <!--底部选项卡-->
            <nav class="mui-bar mui-bar-tab">
                <a class="mui-tab-item mui-active" id="index">
                    <span class="mui-icon mui-icon-home"></span>
                    <span class="mui-tab-label">首页</span>
                </a>
                <a class="mui-tab-item" id="message">
                    <span class="mui-icon mui-icon-chat">
                        <span class="mui-badge mui-badge-red" id="msg_count">0</span>
                    </span>
                    <span class="mui-tab-label">消息</span>
                </a>
                <a class="mui-tab-item">
                    <span class="mui-icon mui-icon-email"></span>
                    <span class="mui-tab-label">邮件</span>
                </a>
                <a class="mui-tab-item" id="login">
                    <span class="mui-icon mui-icon-gear"></span>
                    <span class="mui-tab-label">设置</span>
                </a>
            </nav>
        </body>
        <script type="text/javascript" charset="utf-8">
            var ws = null; // websocket对象
            var msg_data = null;  // 消息数据
            mui.init({
                subpages: [{
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                }]
            });
            mui.plusReady(function() {
                //            console.log(JSON.stringify(plus.webview.currentWebview()))
                if(plus.storage.getItem("user")) { // 判断是否登录
                    console.log('已结登录了!');
                    //连接websocket连接
                    ws = new WebSocket("ws://" + window.ws_serv + "/app/" + plus.storage.getItem("user"))
                    
                    // 发送post请求
                    console.log(window.serv + "/get_msg_list");
                    mui.post(
                        // 访问消息列表
                        window.serv + "/get_msg_list", {
                            user_id: plus.storage.getItem("user")
                        },
                        function(data) {
                            console.log(JSON.stringify(data));
                            //  {"code":0,"data":{"5ba0f1f2e12532418089bf88":1,"count":1},"msg":""}
                            msg_data = data.data;
                            // 修改消息选项卡的角标数字
                            document.getElementById("msg_count").innerText = msg_data.count;
                        }
                    );
                    
                    // 客户端接收服务端数据时触发
                    ws.onmessage = function() {};
                }
                // 自动重连
                ws.onclose = function() {
                    window.location.reload();
                }
            });
    
            // 消息
            document.getElementById("message").addEventListener("tap", function() {
                mui.openWindow({
                    url: "message.html",
                    id: "message.html",
                    styles: window.styles,
                    extras: {
                        // 传输用户id,给message.html
                        user_id: plus.storage.getItem("user"),
                        msg_data: msg_data,
                        //  "data":{"5ba0f1f2e12532418089bf88":1,"count":1}
                    }
                })
            });
    
            document.getElementById("index").addEventListener("tap", function() {
                mui.openWindow({
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                })
            })
    
            document.getElementById("login").addEventListener("tap", function() {
                // 自动登录,判断storage中的user存在,就跳转到user_info,否则跳转login
                if(plus.storage.getItem("user")) {
                    mui.openWindow({
                        url: "user_info.html",
                        id: "user_info.html",
                        styles: window.styles,
                        extras: {
                            user_id: plus.storage.getItem("user")
                        }
                    })
                } else {
                    mui.openWindow({
                        url: "login.html",
                        id: "login.html",
                        styles: window.styles
                    })
                }
            })
    
            document.addEventListener("login", function(data) {
                // fire事件接收消息,使用data.detail
                // index是为做显示区分
                mui.toast("index" + data.detail.msg)
            });
    
            document.addEventListener("send_music", function(data) { //监听send_music事件
                var music_name = data.detail.music_name; //获取player.html使用fire发送的music_name值
                var toy_id = data.detail.toy_id; //获取发送的玩具id
    
                send_str = { //构造数据
                    data: music_name,
                    to_user: toy_id, // 目标用户,这里统一格式
                    msg_type: "music", // 类型为音乐
                }
                // 发送数据给后端,注意要json序列化
                ws.send(JSON.stringify(send_str));
            });
    
            document.addEventListener("send_msg", function(data) { //发送消息
                var filename = data.detail.filename
                var to_user = data.detail.to_user
                send_str = {
                    to_user: to_user
                }
                ws.send(JSON.stringify(send_str))
                plus.io.resolveLocalFileSystemURL(filename, function(entry) {
                    // 可通过entry对象操作test.html文件 
                    entry.file(function(file) {
                        // FileReader文件系统中的读取文件对象,用于获取文件的内容
                        var fileReader = new plus.io.FileReader();
    
                        //                        alert("getFile:" + JSON.stringify(file));
                        // readAsDataURL: 以URL编码格式读取文件数据内容
                        fileReader.readAsDataURL(file, 'utf-8');
                        // onloadend: 文件读取操作完成时的回调函数
                        fileReader.onloadend = function(evt) {
                            console.log(evt.target.result);
                            var b = dataURLtoBlob(evt.target.result);
                            ws.send(b); // 发送blob数据
    
                        }
                        //                        alert(file.size + '--' + file.name)
                    });
                });
    
            });
            
            // 监听cut_msg_count事件,由message.html向index.html执行fire
            document.addEventListener("cut_msg_count", function(data) {
                var msg_count = document.getElementById("msg_count");
                var cut = parseInt(data.detail.cut);  // parseInt表示强制转换
                var count = parseInt(msg_count.innerText);  // 默认获取innerText是字符串,需要强制转换
                msg_count.innerText = count - cut;  // 总数 减去 点击聊天会话的数量,比如小甜甜的
            });
    
            function dataURLtoBlob(dataurl) { // 数据转换为Blob
                // 逻辑很复杂,这里不解释了。直接用就可以了!
                var arr = dataurl.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);
                while(n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                var a = new Blob([u8arr], {
                    type: mime
                });
                return a
            }
        </script>
    
    </html>
    View Code

     

     效果如下:

    可以发现,底部选项卡,也变成0了

     

     

    后端操作

    那是因为后端redis的数据没有更改。

     

    进入flask项目,修改 utils-->chat_redis.py

    from setting import REDIS_DB
    import json
    
    
    def save_msg(sender, to_user):  # 保存消息
        # 1.查询一下xiao的Redis是否有数据
        user_msg_redis = REDIS_DB.get(to_user)
        if user_msg_redis:
            # 2.将xiao的数据反序列化成字典 { sender : n }
            user_msg_dict = json.loads(user_msg_redis)
            # 3.判断有没有 sender 的用户发来的消息数量
            if user_msg_dict.get(sender):
                # 数量加1
                user_msg_dict[sender] += 1
            else:
                # 第一次,初始值为1
                user_msg_dict[sender] = 1
        # 4.如果xiao是刚建立好的用户,他是没有消息的,字典是空
        else:
            user_msg_dict = {sender: 1}
    
        # 5.序列化用户消息字典user_msg_dict
        user_msg_redis = json.dumps(user_msg_dict)
        # 6.存回Redis
        REDIS_DB.set(to_user, user_msg_redis)
    
    def get_msg_list(user):  # 获取消息
        user_msg_redis = REDIS_DB.get(user)
        if user_msg_redis:
            user_msg_dict = json.loads(user_msg_redis)
            # 统计数量
            user_msg_dict["count"] = sum(user_msg_dict.values())
        else:
            user_msg_dict = {"count":0}
    
        return user_msg_dict
    
    def get_user_msg_one(sender, to_user):  # 获取用户一个好友消息
        user_msg_redis = REDIS_DB.get(to_user)
        if user_msg_redis:
            user_msg_dict = json.loads(user_msg_redis)
            if user_msg_dict.get(sender):
                # return user_msg_dict.get(sender)
                user_msg_dict[sender] = 0
    
        else:
            user_msg_dict = {sender:0}
    
        user_msg_redis = json.dumps(user_msg_dict)
        REDIS_DB.set(to_user,user_msg_redis)  # 修改redis
    View Code

     

    修改 serv-->chat.py,增加 chat_redis.get_user_msg_one

    from flask import Blueprint, request, jsonify
    from setting import MONGO_DB
    from setting import RET
    from bson import ObjectId
    from utils import chat_redis
    
    cht = Blueprint("cht", __name__)
    
    
    @cht.route("/chat_list", methods=["POST"])
    def chat_list():  # 聊天记录列表
        user_id = request.form.get("user_id")
        friend_id = request.form.get("friend_id")
        print(friend_id)
    
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, friend_id]}})
        fri = MONGO_DB.toys.find_one({"_id": ObjectId(friend_id)})
        baby_name = fri.get("baby_name")
        cl = chat_window.get("chat_list")
    
        RET["code"] = 0
        RET["msg"] = baby_name
        RET["data"] = cl
    
        # 获取用户单个好友记录,修改redis的值
        chat_redis.get_user_msg_one(friend_id,user_id)
    
        return jsonify(RET)
    
    
    @cht.route("/get_msg", methods=["POST"])
    def get_msg():  # 获取聊天语言文件
        user_id = request.form.get("user_id")
        sender = request.form.get("sender")
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, sender]}})
        new_msg = chat_window.get("chat_list")[-1]
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = new_msg.get("msg")
    
        return jsonify(RET)
    
    @cht.route("/get_msg_list", methods=["POST"])
    def get_msg_list():
        user_id = request.form.get("user_id")
        user_msg_dict = chat_redis.get_msg_list(user_id)
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = user_msg_dict
    
        return jsonify(RET)
    View Code

     

    打开夜神模拟器,重启 里面的HBuilder APP。再次点击,查看redis

    127.0.0.1:6379> get 5b9bb768e1253281608e96eb
    "{\"5ba0f1f2e12532418089bf88\": 0}"

    发现已经更新为0了

     

    消息增加

    但是发消息,可能不止一条。如果有消息,角标的数字应该自动加。

    进入 HBuilder项目MyApp,修改index.html

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
            <title></title>
            <script src="js/mui.js"></script>
            <link href="css/mui.min.css" rel="stylesheet" />
        </head>
    
        <body>
            <!--底部选项卡-->
            <nav class="mui-bar mui-bar-tab">
                <a class="mui-tab-item mui-active" id="index">
                    <span class="mui-icon mui-icon-home"></span>
                    <span class="mui-tab-label">首页</span>
                </a>
                <a class="mui-tab-item" id="message">
                    <span class="mui-icon mui-icon-chat">
                        <span class="mui-badge mui-badge-red" id="msg_count">0</span>
                    </span>
                    <span class="mui-tab-label">消息</span>
                </a>
                <a class="mui-tab-item">
                    <span class="mui-icon mui-icon-email"></span>
                    <span class="mui-tab-label">邮件</span>
                </a>
                <a class="mui-tab-item" id="login">
                    <span class="mui-icon mui-icon-gear"></span>
                    <span class="mui-tab-label">设置</span>
                </a>
            </nav>
        </body>
        <script type="text/javascript" charset="utf-8">
            var ws = null; // websocket对象
            var msg_data = null;  // 消息数据
            mui.init({
                subpages: [{
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                }]
            });
            mui.plusReady(function() {
                //            console.log(JSON.stringify(plus.webview.currentWebview()))
                if(plus.storage.getItem("user")) { // 判断是否登录
                    console.log('已结登录了!');
                    //连接websocket连接
                    ws = new WebSocket("ws://" + window.ws_serv + "/app/" + plus.storage.getItem("user"))
                    
                    // 发送post请求
                    console.log(window.serv + "/get_msg_list");
                    mui.post(
                        // 访问消息列表
                        window.serv + "/get_msg_list", {
                            user_id: plus.storage.getItem("user")
                        },
                        function(data) {
                            console.log(JSON.stringify(data));
                            //  {"code":0,"data":{"5ba0f1f2e12532418089bf88":1,"count":1},"msg":""}
                            msg_data = data.data;
                            // 修改消息选项卡的角标数字
                            document.getElementById("msg_count").innerText = msg_data.count;
                        }
                    );
                    
                    // 客户端接收服务端数据时触发
                    ws.onmessage = function(data) {
                        console.log(data.data);
                        var msg = JSON.parse(data.data);
                        var chat = plus.webview.getWebviewById("chat.html");
                        mui.fire(chat, "new_msg", {  // 向chat.html传值
                            data: msg
                        });
                        var msg_count = document.getElementById("msg_count");
                        // 当前页面加1
                        msg_count.innerText = parseInt(msg_count.innerText) + 1;
                        // 加1,用于message.html显示
                        msg_data[msg.from_user]++;
                    };
                }
                // 自动重连
                ws.onclose = function() {
                    window.location.reload();
                }
            });
    
            // 消息
            document.getElementById("message").addEventListener("tap", function() {
                mui.openWindow({
                    url: "message.html",
                    id: "message.html",
                    styles: window.styles,
                    extras: {
                        // 传输用户id,给message.html
                        user_id: plus.storage.getItem("user"),
                        msg_data: msg_data,
                        //  "data":{"5ba0f1f2e12532418089bf88":1,"count":1}
                    }
                })
            });
    
            document.getElementById("index").addEventListener("tap", function() {
                mui.openWindow({
                    url: "main.html",
                    id: "main.html",
                    styles: window.styles
                })
            })
    
            document.getElementById("login").addEventListener("tap", function() {
                // 自动登录,判断storage中的user存在,就跳转到user_info,否则跳转login
                if(plus.storage.getItem("user")) {
                    mui.openWindow({
                        url: "user_info.html",
                        id: "user_info.html",
                        styles: window.styles,
                        extras: {
                            user_id: plus.storage.getItem("user")
                        }
                    })
                } else {
                    mui.openWindow({
                        url: "login.html",
                        id: "login.html",
                        styles: window.styles
                    })
                }
            })
    
            document.addEventListener("login", function(data) {
                // fire事件接收消息,使用data.detail
                // index是为做显示区分
                mui.toast("index" + data.detail.msg)
            });
    
            document.addEventListener("send_music", function(data) { //监听send_music事件
                var music_name = data.detail.music_name; //获取player.html使用fire发送的music_name值
                var toy_id = data.detail.toy_id; //获取发送的玩具id
    
                send_str = { //构造数据
                    data: music_name,
                    to_user: toy_id, // 目标用户,这里统一格式
                    msg_type: "music", // 类型为音乐
                }
                // 发送数据给后端,注意要json序列化
                ws.send(JSON.stringify(send_str));
            });
    
            document.addEventListener("send_msg", function(data) { //发送消息
                var filename = data.detail.filename
                var to_user = data.detail.to_user
                send_str = {
                    to_user: to_user
                }
                ws.send(JSON.stringify(send_str))
                plus.io.resolveLocalFileSystemURL(filename, function(entry) {
                    // 可通过entry对象操作test.html文件 
                    entry.file(function(file) {
                        // FileReader文件系统中的读取文件对象,用于获取文件的内容
                        var fileReader = new plus.io.FileReader();
    
                        //                        alert("getFile:" + JSON.stringify(file));
                        // readAsDataURL: 以URL编码格式读取文件数据内容
                        fileReader.readAsDataURL(file, 'utf-8');
                        // onloadend: 文件读取操作完成时的回调函数
                        fileReader.onloadend = function(evt) {
                            console.log(evt.target.result);
                            var b = dataURLtoBlob(evt.target.result);
                            ws.send(b); // 发送blob数据
    
                        }
                        //                        alert(file.size + '--' + file.name)
                    });
                });
    
            });
            
            // 监听cut_msg_count事件,由message.html向index.html执行fire
            document.addEventListener("cut_msg_count", function(data) {
                var msg_count = document.getElementById("msg_count");
                var cut = parseInt(data.detail.cut);  // parseInt表示强制转换
                var count = parseInt(msg_count.innerText);  // 默认获取innerText是字符串,需要强制转换
                msg_count.innerText = count - cut;  // 总数 减去 点击聊天会话的数量,比如小甜甜的
            });
    
            function dataURLtoBlob(dataurl) { // 数据转换为Blob
                // 逻辑很复杂,这里不解释了。直接用就可以了!
                var arr = dataurl.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);
                while(n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                var a = new Blob([u8arr], {
                    type: mime
                });
                return a
            }
        </script>
    
    </html>
    View Code

     

    打开网页,发送消息

    发送2条消息

     

    这里会实时变化

     这里还是0,是因为这个页面的plusReady只会加载一次。这个是一个小bug

     

    如果关闭进程,再次开启,就会有了!

     

    进入 flask后端,修改im_serv.py

    from flask import Flask, request
    from geventwebsocket.websocket import WebSocket
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    import json, os
    from uuid import uuid4
    from setting import AUDIO_FILE,CHAT_FILE
    from serv import content
    from utils import baidu_ai
    from utils import chat_redis
    import setting
    from bson import ObjectId
    import time
    
    app = Flask(__name__)
    
    user_socket_dict = {}  # 空字典,用来存放用户名和发送消息
    
    
    @app.route("/toy/<tid>")
    def toy(tid):  # 玩具连接
        # 获取请求的WebSocket对象
        user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
        if user_socket:
            # 设置键值对
            user_socket_dict[tid] = user_socket
            print(user_socket_dict)
            # {'123456': <geventwebsocket.websocket.WebSocket object at 0x00000176ABD92E18>}
    
        file_name = ""
        to_user = ""
        # 循环,接收消息
        while True:
            msg = user_socket.receive()
            if type(msg) == bytearray:
                file_name = f"{uuid4()}.wav"
                file_path = os.path.join(CHAT_FILE, file_name)
                with open(file_path, "wb") as f:
                    f.write(msg)
            else:
                msg_dict = json.loads(msg)
                to_user = msg_dict.get("to_user")
                msg_type = msg_dict.get("msg_type")
    
            if to_user and file_name:
                other_user_socket = user_socket_dict.get(to_user)
                if msg_type == "ai":
                    q = baidu_ai.audio2text(file_path)
                    print(q)
                    ret = baidu_ai.my_nlp(q, tid)
                    other_user_socket.send(json.dumps(ret))
                else:
                    send_str = {
                        "code": 0,
                        "from_user": tid,
                        "msg_type": "chat",
                        "data": file_name
                    }
    
                    if other_user_socket:  # 当websocket连接存在时
                        chat_redis.save_msg(tid, to_user)  # 保存消息到redis
                        # 发送数据
                        other_user_socket.send(json.dumps(send_str))
                    else:
                        # 离线消息
                        chat_redis.save_msg(tid, to_user)
    
                    # 保存聊天记录到MongoDB
                    _add_chat(tid, to_user, send_str.get("data"))
    
                to_user = ""
                file_name = ""
    
    
    @app.route("/app/<uid>")
    def user_app(uid):  # 手机app连接
        user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
        if user_socket:
            user_socket_dict[uid] = user_socket
            # { uid : websocket}
            print(user_socket_dict)
    
        file_name = ""
        to_user = ""
    
        while True:  # 手机听歌 把歌曲发送给 玩具 1.将文件直接发送给玩具 2.将当前听的歌曲名称或ID发送到玩具
            msg = user_socket.receive()
            if type(msg) == bytearray:  # 判断类型为bytearray
                file_name = f"{uuid4()}.amr"  # 文件后缀为amr,安卓和ios通用
                file_path = os.path.join(CHAT_FILE, file_name)  # 存放在chat目录
                print(msg)
                with open(file_path, "wb") as f:
                    f.write(msg)  # 写入文件
    
                # 将amr转换为mp3,因为html中的audio不支持amr
                os.system(f"ffmpeg -i {file_path} {file_path}.mp3")
    
            else:
                msg_dict = json.loads(msg)
                to_user = msg_dict.get("to_user")  # 获取目标用户
    
                if msg_dict.get("msg_type") == "music":
                    other_user_socket = user_socket_dict.get(to_user)
    
                    send_str = {
                        "code": 0,
                        "from_user": uid,
                        "msg_type": "music",
                        "data": msg_dict.get("data")
                    }
                    other_user_socket.send(json.dumps(send_str))
    
                # res = content._content_one(content_id)
            if file_name and to_user:  # 如果文件名和发送用户同上存在时
                # 查询玩具信息
                res = setting.MONGO_DB.toys.find_one({"_id": ObjectId(to_user)})
                # 获取friend_remark
                fri = [i.get("friend_remark") for i in res.get("friend_list") if i.get("friend_id") == uid][0]
                msg_file_name = baidu_ai.text2audio(f"你有来自{fri}的消息")
    
                # 获取websocket对象
                other_user_socket = user_socket_dict.get(to_user)
                # 构造数据
                send_str = {
                    "code": 0,
                    "from_user": uid,
                    "msg_type": "chat", # 聊天类型
                    # 后缀必须是mp3的
                    "data": msg_file_name
                }
                if other_user_socket:
                    chat_redis.save_msg(uid, to_user)
                    # 发送数据给前端页面
                    other_user_socket.send(json.dumps(send_str))
                else:
                    # 保存redis
                    chat_redis.save_msg(uid, to_user)
    
                # 添加聊天记录到数据库
                _add_chat(uid, to_user, f"{file_name}.mp3")
                # 最后一定要清空这2个变量,否则造成混乱
                file_name = ""
                to_user = ""
    
    def _add_chat(sender, to_user, msg):  # 添加聊天记录到数据库
        chat_window = setting.MONGO_DB.chat.find_one({"user_list": {"$all": [sender, to_user]}})
        if not chat_window.get("chat_list"):
            chat_window["chat_list"] = [{
                "sender": sender,
                "msg": msg,
                "updated_at": time.time(),
            }]
            res = setting.MONGO_DB.chat.update_one({"_id": ObjectId(chat_window.get("_id"))}, {"$set": chat_window})
        else:
            chat = {
                "sender": sender,
                "msg": msg,
                "updated_at": time.time(),
            }
            res = setting.MONGO_DB.chat.update_one({"_id": ObjectId(chat_window.get("_id"))}, {"$push": {"chat_list": chat}})
    
        return res
    
    if __name__ == '__main__':
        # 创建一个WebSocket服务器
        http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler)
        # 开始监听HTTP请求
        http_serv.serve_forever()
    
    
    '''
    {
        "code": 0,
        "from_user": uid,  # APP用户id
        "data": music_name  # 歌曲名
    }
    '''
    View Code

     

    给小甜甜发送消息,可能不止一条。后端收取消息,要有多条

     

    修改 serv-->chat.py,改为[-count:] 。如果是2条,就是[-2:]。表示最后2条!

    from flask import Blueprint, request, jsonify
    from setting import MONGO_DB
    from setting import RET
    from bson import ObjectId
    from utils import chat_redis
    
    cht = Blueprint("cht", __name__)
    
    
    @cht.route("/chat_list", methods=["POST"])
    def chat_list():  # 聊天记录列表
        user_id = request.form.get("user_id")
        friend_id = request.form.get("friend_id")
        print(friend_id)
    
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, friend_id]}})
        fri = MONGO_DB.toys.find_one({"_id": ObjectId(friend_id)})
        baby_name = fri.get("baby_name")
        cl = chat_window.get("chat_list")
    
        RET["code"] = 0
        RET["msg"] = baby_name
        RET["data"] = cl
    
        # 获取用户单个好友记录,修改redis的值
        chat_redis.get_user_msg_one(friend_id,user_id)
    
        return jsonify(RET)
    
    
    @cht.route("/get_msg", methods=["POST"])
    def get_msg():  # 获取聊天语言文件
        user_id = request.form.get("user_id")
        sender = request.form.get("sender")
        count = 1  # 初始值
        if not sender:
            msg_dict = chat_redis.get_msg_list(user_id)
            print(msg_dict,"msg_dict")
            sender = list(msg_dict.keys())[0]
            count = msg_dict[sender]
        else:
            # 获取用户某个好友的值
            count = chat_redis.get_user_msg_one(sender,user_id)
    
        # $all 表示多个条件都成立时。这里表示user_list字段中user_id和sender必须都存在才行!
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, sender]}})
        # [-count:] 表示获取最后的几条消息。比如: -1: 表示最后一条
        new_msg = chat_window.get("chat_list")[-count:]
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = new_msg
    
        # chat_redis.get_user_msg_one(sender,user_id)
    
        return jsonify(RET)
    
    @cht.route("/get_msg_list", methods=["POST"])
    def get_msg_list():
        user_id = request.form.get("user_id")
        user_msg_dict = chat_redis.get_msg_list(user_id)
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = user_msg_dict
    
        return jsonify(RET)
    View Code

     

     修改 utils-->chat_redis.py

    from setting import REDIS_DB
    import json
    
    
    def save_msg(sender, to_user):  # 保存消息
        # 1.查询一下xiao的Redis是否有数据
        user_msg_redis = REDIS_DB.get(to_user)
        if user_msg_redis:
            # 2.将xiao的数据反序列化成字典 { sender : n }
            user_msg_dict = json.loads(user_msg_redis)
            # 3.判断有没有 sender 的用户发来的消息数量
            if user_msg_dict.get(sender):
                # 数量加1
                user_msg_dict[sender] += 1
            else:
                # 第一次,初始值为1
                user_msg_dict[sender] = 1
        # 4.如果xiao是刚建立好的用户,他是没有消息的,字典是空
        else:
            user_msg_dict = {sender: 1}
    
        # 5.序列化用户消息字典user_msg_dict
        user_msg_redis = json.dumps(user_msg_dict)
        # 6.存回Redis
        REDIS_DB.set(to_user, user_msg_redis)
    
    def get_msg_list(user):  # 获取消息
        user_msg_redis = REDIS_DB.get(user)
        if user_msg_redis:
            user_msg_dict = json.loads(user_msg_redis)
            # 统计数量
            user_msg_dict["count"] = sum(user_msg_dict.values())
        else:
            user_msg_dict = {"count":0}
    
        return user_msg_dict
    
    def get_user_msg_one(sender, to_user):  # 获取用户一个好友消息
        user_msg_redis = REDIS_DB.get(to_user)
        if user_msg_redis:
            user_msg_dict = json.loads(user_msg_redis)
            if user_msg_dict.get(sender):
                return user_msg_dict.get(sender)
                # user_msg_dict[sender] = 0
    
        else:
            user_msg_dict = {sender:0}
    
        user_msg_redis = json.dumps(user_msg_dict)
        REDIS_DB.set(to_user,user_msg_redis)  # 修改redis
    View Code

     

    修改index.html,使用player.onended。它会接收多条

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <audio src="" autoplay="autoplay" controls id="player"></audio>
    <br>
    <input type="text" id="device_id"/>
    <button οnclick="start_toy()">玩具开机键</button>
    <br>
    <button οnclick="start_reco()">开始废话</button>
    <br>
    <button οnclick="stop_reco()">发送语音</button>
    <br>
    <button οnclick="start_reco()">录制消息</button>
    <span id="to_user"></span>
    <br>
    <button οnclick="send_reco()">发送语音消息</button>
    <br>
    <button οnclick="recv_msg()">收取消息</button>
    </body>
    <script src="/static/recorder.js"></script>
    <script src="/static/jquery.min.js"></script>
    <script type="application/javascript">
        var serv = "http://127.0.0.1:9527";
        var ws_serv = "ws://127.0.0.1:9528";
    
        // 获取音频文件
        var get_music = serv + "/get_audio/";
        var get_chat = serv + "/get_chat/";
    
        var ws = null;  // WebSocket 对象
        var reco = null;
        // 创建AudioContext对象
        var audio_context = new AudioContext();
    
        var toy_id = null;
    
        //要获取音频和视频
        navigator.getUserMedia = (navigator.getUserMedia ||
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia ||
            navigator.msGetUserMedia);
    
        // 拿到媒体对象,允许音频对象
        navigator.getUserMedia({audio: true}, create_stream, function (err) {
            console.log(err)
        });
    
        //创建媒体流容器
        function create_stream(user_media) {
            var stream_input = audio_context.createMediaStreamSource(user_media);
            // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流
            reco = new Recorder(stream_input);
    
        }
    
        function start_reco() {  //开始录音
            reco.record();  //往里面写流
        }
    
        function stop_reco() {  //停止录音
            reco.stop();  //停止写入流
            get_audio();  //调用自定义方法
            reco.clear();  //清空容器
        }
    
        {#function get_audio() {  // 获取音频#}
        {#    reco.exportWAV(function (wav_file) {#}
        {#        ws.send(wav_file);  //使用websocket连接发送数据给后端#}
        {#    })#}
        {# }#}
    
        function send_reco() {
            reco.stop();
            send_audio();
            reco.clear();
        }
    
        function send_audio() {
            var to_user = document.getElementById("to_user").innerText;
            var send_str = {
                "to_user": to_user
            };
            ws.send(JSON.stringify(send_str));
            reco.exportWAV(function (wav_file) {
                ws.send(wav_file);
            })
        }
    
        function get_audio() {
    
            var send_str = {
                "to_user": toy_id,
                "msg_type": "ai"
            };
            ws.send(JSON.stringify(send_str));
            reco.exportWAV(function (wav_file) {
                ws.send(wav_file);
            })
        }
    
    
        function start_toy() {  // 玩具开机
            // 获取输入的设备id
            var device_id = document.getElementById("device_id").value;
            // 发送post请求
            $.post(
                // 这里的地址必须是127.0.0.1,否则会有跨域问题
                "http://127.0.0.1:9527/device_toy_id",
                // 发送设备id
                {device_id: device_id},
                function (data) {
                    console.log(data);
                    toy_id = data.data.toy_id;  // 玩具id
                    // 修改audio标签的src属性
                    document.getElementById("player").src = get_music + data.data.audio;
                    if (toy_id) {  // 判断玩具id存在时
                        ws = new WebSocket(ws_serv + "/toy/" + toy_id);
                        ws.onmessage = function (data) {
                            // console.log(get_music + data.data);
                            var content = JSON.parse(data.data);  //反序列化数据
                            // 判断消息类型
                            if (content.msg_type == "chat") {
                                document.getElementById("player").src = get_chat + content.data;
                                document.getElementById("to_user").innerText = content.from_user;
                                console.log(content.from_user + "给你发送了一条消息");
                            }
                            if (content.msg_type == "music") {
                                document.getElementById("player").src = get_music + content.data;
                                console.log(content.from_user + "给你点播了歌儿");
                            }
                        };
                        ws.onclose = function () {
                            window.location.reload();
                        }
                    }
                }, "json"
                // 规定预期的服务器响应的数据类型为json
            );
        }
    
        function recv_msg() {
            var to_user = document.getElementById("to_user").innerText;
            var player = document.getElementById("player");
    
            to_user = document.getElementById("to_user").innerText;
            $.post(
                serv + "/get_msg",
                {user_id: toy_id, sender: to_user},
                function (data) {
                    // shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
                    var msg = data.data.shift();
                    document.getElementById("to_user").innerText = msg.sender;
                    player.src = get_chat + msg.msg;  //修改audio标签src属性
                    // onended 事件在视频/音频(audio/video)播放结束时触发
                    player.onended = function () {
                        // 如果长度大于0,也就是有1条或者多条时
                        if(data.data.length > 0){
                            //修改audio标签src属性,有多条时,会轮询触发
                           player.src = get_chat + data.data.shift().msg;
                        }else{
                           return null;
                        }
                    }
                }, "json"
            )
        }
    
    
    </script>
    </html>
    View Code

    重启 manager.py和im_serv.py

     

    重新访问网页,让玩具开机。连续录制2个语音

    再点击收取消息,网页会先播放一条,再紧着播放第二条!

     

    二、玩具端消息推送

    APP发送语音后,页面语音提示,你有来自 xx 的消息

     

    修改 serv-->chat.py,修改get_msg视图函数

    from flask import Blueprint, request, jsonify
    from setting import MONGO_DB
    from setting import RET
    from bson import ObjectId
    from utils import chat_redis
    
    cht = Blueprint("cht", __name__)
    
    
    @cht.route("/chat_list", methods=["POST"])
    def chat_list():  # 聊天记录列表
        user_id = request.form.get("user_id")
        friend_id = request.form.get("friend_id")
        print(friend_id)
    
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, friend_id]}})
        fri = MONGO_DB.toys.find_one({"_id": ObjectId(friend_id)})
        baby_name = fri.get("baby_name")
        cl = chat_window.get("chat_list")
    
        RET["code"] = 0
        RET["msg"] = baby_name
        RET["data"] = cl
    
        # 获取用户单个好友记录,修改redis的值
        chat_redis.get_user_msg_one(friend_id,user_id)
    
        return jsonify(RET)
    
    
    @cht.route("/get_msg", methods=["POST"])
    def get_msg():  # 获取聊天语言文件
        user_id = request.form.get("user_id")
        sender = request.form.get("sender")
        count = 1  # 初始值
        if not sender:
            msg_dict = chat_redis.get_msg_list(user_id)
            # print(msg_dict,"msg_dict")
            # 未读数量
            sender = [i for i in msg_dict.keys() if msg_dict[i] != 0 and i != "count"]
            if sender:
                sender = sender[0]
                count = msg_dict[sender]
            else:
                pass  # 没有任何消息了,可以调用合成语言,提示一下
                # filename= baidu_ai.text2audio("")
                # new_msg = [{sender:"",msg:filename}]
        else:
            # 获取用户某个好友的值
            count = chat_redis.get_user_msg_one(sender,user_id)
    
        # $all 表示多个条件都成立时。这里表示user_list字段中user_id和sender必须都存在才行!
        chat_window = MONGO_DB.chat.find_one({"user_list": {"$all": [user_id, sender]}})
        # [-count:] 表示获取最后的几条消息。比如: -1: 表示最后一条
        new_msg = chat_window.get("chat_list")[-count:]
        # 这里可以提示,您收到来自xx的几条消息
        # filename= baidu_ai.text2audio("")
        # new_msg.insert(0,{
        #     "sender":sender,
        #     "msg":filename
        # })
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = new_msg
    
        chat_redis.get_user_msg_one(sender,user_id)
    
        return jsonify(RET)
    
    @cht.route("/get_msg_list", methods=["POST"])
    def get_msg_list():
        user_id = request.form.get("user_id")
        user_msg_dict = chat_redis.get_msg_list(user_id)
    
        RET["code"] = 0
        RET["msg"] = ""
        RET["data"] = user_msg_dict
    
        return jsonify(RET)
    View Code

    重启 manager.py和im_serv.py

     

    使用App发送2条消息

     

    玩具页面会有语音提示,你有来自 小鱼的消息

    点击收取消息。会自动播放2条语音!

     

     

    今日总结:

    1.向app推送消息
        Redis 中存储消息:
            to_user : { sender : 1 }
            
        消息按钮 未读消息
            在message点击打开 chat_window的时候 发起一个fire(cut)事件给index页面
            index页面 根据 cut 值进行删减 角标的数字
            
        chat_window 未读消息
            message页面将未读消息数字置空 = 0 
            chat_window 页面 发起的 chat_list 请求,加入逻辑 将redis中的未读消息置空 = 0
        
    2.玩具端消息推送
        消息列表 - 区分用户
        array.shift()    删除array中的第一个元素并返回
        批量收取一个用户的消息
        AudioContext.onended = function(){
            AudioContext.src = "music.mp3"
        }
        
        批量收取消息的逻辑:
            1.从sender未读消息中拿出未读数量 未读数量不是 0 
            2.从chat_list中拿出最后的几条未读消息
    View Code

     

    完整代码,参考github:

    https://github.com/987334176/Intelligent_toy/archive/v1.5.zip

     

    posted @ 2018-09-21 15:25 肖祥 阅读(...) 评论(...) 编辑 收藏
    展开全文
  • 可以为手机端的app使用者推送消息,而不是通过手机上的app对用户发送消息。项目名称改为getuitest
  •  最近在做个项目,当客服端收到防盗的消息通知时向手机app推送一个消息,告知有防盗报警。这么小的功能没必要自己写个推送端,极光推送免费而且推送的成功率高,已经能满足我们的需求了。  极光推送的文档大家...
    1. 简介 

        最近在做个项目,当客服端收到防盗的消息通知时向手机app推送一个消息,告知有防盗报警。这么小的功能没必要自己写个推送端,极光推送免费而且推送的成功率高,已经能满足我们的需求了。

         极光推送的文档大家可以到极光推送的官网查看(http://docs.jiguang.cn/),由于我们这是是使用Qt C++开发的极光推送并没有提供c++的封装,这里我们选择rest API的方式推送,rest API的推送方式的demo以及json格式在http://docs.jiguang.cn/server/rest_api_v3_push/可以看 到。rest API的推送方式可以直接通过curl等工具构造适合的json发送即可。以下是可
    2. 极光推送平台
      注册极光推送后进入自己的管理平台即可看到自己的appKey和masterSecret以及下载自己的app Example

    3. QT极光推送的简单封装。
      //********************************************************************
      // Copyright (C) 2016   
      // All rights reserved.
      //
      // 文件名称:qJpush.h
      // 摘    要:对极光推送的封装
      //
      // 当前版本:v1.0
      // 修 改 人:
      // 修改时间:2016-03-31
      // 修改描述:
      //
      // 历史版本:
      // 历史作者:
      // 上次时间:2016-03-31
      // 历史描述:
      //********************************************************************
      #ifndef QJPUSH_H
      #define QJPUSH_H
      
      #include <QObject>
      #include <QtNetwork/QNetworkAccessManager>
      #include <QtNetwork/QNetworkRequest>
      #include <QtNetwork/QNetworkReply>
      #include <QUrl>
      #include <QJsonParseError>
      #include <QJsonObject>
      #include "../librarys_global.h"
      
      class LIBRARYS_EXPORT QJpush : public QObject
      {
          Q_OBJECT
      
      public:
          QJpush(QObject *parent = nullptr);
          ~QJpush();
      
          void pushAll(
              const QString &_alert,
              const QString &_title,
              const QString &_msg_content = "",
              const QString &_msg_title = "");     //所有用户
          void pushTag(
              const QString &_tag,
              const QString &_alert,
              const QString &_title,
              const QString &_msg_content = "",
              const QString &_msg_title = "");    //根据标签发送
      
      private slots:
          void onFinished(QNetworkReply *reply);
          void slotSSLErrorDeal(QNetworkReply * reply, const QList<QSslError> & errors);
      
      private:
          void jPush(
              const QString &_alert,
              const QString &_title,
              const QString &_msg_content,
              const QString &_msg_title,
              const QString &_tag /*= "all"*/);
      
      
          QNetworkAccessManager *m_pNetworkAccManger;
          QNetworkRequest request;
          QUrl m_url;
      };
      
      #endif // QJPUSH_H
      //********************************************************************
      // Copyright (C) 2016
      // All rights reserved.
      //
      // 文件名称:qJpush.cpp
      // 摘    要:对极光推送的封装
      //
      // 当前版本:v1.0
      // 修 改 人:
      // 修改时间:2016-03-31
      // 修改描述:
      //
      // 历史版本:
      // 历史作者:
      // 上次时间:2016-03-31
      // 历史描述:
      //********************************************************************
      #include "qJpush.h"
      #include <QJsonArray>
      #include <QFile>
      #include <QDir>
      
      #include "singleton.h"
      #include "qUtils.h"
      #include "qlog.h"
      
      const QString host = "https://api.jpush.cn/v3/push";
      const QString appKey = "自己的appKey";
      const QString masterSecret = "自己的masterSecret";
      
      QString base64_encode(const QString &_srcStr)
      {
          QByteArray tmp_in(_srcStr.toUtf8());
          return QString(tmp_in.toBase64());
      }
      
      QJpush::QJpush(QObject *parent)
      : QObject(parent),
      m_pNetworkAccManger(new QNetworkAccessManager(this))
      {
          //配置ssl协议
          QSslConfiguration config;
      
          config.setPeerVerifyMode(QSslSocket::VerifyNone);
          config.setProtocol(QSsl::TlsV1_0);
          request.setSslConfiguration(config);
      
          m_url.setUrl(host);
          request.setUrl(m_url);
      
          QByteArray authorization;
          authorization = QString("Basic %1").arg(base64_encode(appKey + ":" + masterSecret)).toLatin1();
      
          request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");    //声明发送头为json格式
          request.setRawHeader("Authorization", authorization);    //设置密钥
      
          connect(m_pNetworkAccManger, SIGNAL(finished(QNetworkReply *)), this, SLOT(onFinished(QNetworkReply *)));
          connect(m_pNetworkAccManger, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
              this, SLOT(slotSSLErrorDeal(QNetworkReply *, const QList<QSslError> &)));
      }
      
      QJpush::~QJpush()
      {
      
      }
      
      //************************************
      // 函数名称: pushAll
      // 返回类型: void
      // 参数信息: 
      // 函数说明: 向所有的用户推送消息
      //************************************
      void QJpush::pushAll(
          const QString &_alert,
          const QString &_title,
          const QString &_msg_content,
          const QString &_msg_title)
      {
          jPush(_alert, _title, _msg_content, _msg_title, "all");
      }
      
      //************************************
      // 函数名称: pushTag
      // 返回类型: void
      // 参数信息: 
      // 函数说明: 向特定的tag推送消息
      //************************************
      void QJpush::pushTag(
          const QString &_tag,
          const QString &_alert,
          const QString &_title,
          const QString &_msg_content,
          const QString &_msg_title)
      {
          jPush(_alert, _title, _msg_content, _msg_title, _tag);
      }
      
      void QJpush::jPush(
          const QString &_alert,
          const QString &_title,
          const QString &_msg_content,
          const QString &_msg_title,
          const QString &_tag /*= "all"*/)
      {
          QJsonObject rootJsonObj, noteJsonObj, adrdNoteObj, msgNodeObj;
          QJsonDocument jsonDoc;
          QByteArray jsonArr;
      
          rootJsonObj.insert("platform", "all");    //android 和ios平台
      
          //设置tag
          if (_tag == "add")
          {
              rootJsonObj.insert("audience", "all");    //
          }
          else
          {
              QJsonArray arrObj;
              QJsonObject tagNodeObj;
      
              QStringList slt = _tag.split("/");
              size_t sz = slt.size();
              for (int i = 0; i < sz; ++i)
              {
                  arrObj.insert(i, slt.at(i));
              }
      
              tagNodeObj.insert("tag", arrObj);
              rootJsonObj.insert("audience", tagNodeObj);    //
          }
      
          //通知
          adrdNoteObj.insert("alert", _alert);
          adrdNoteObj.insert("title", _title);
      
          //消息
          if (!_msg_title.isEmpty() && !_msg_content.isEmpty())
          {
              msgNodeObj.insert("msg_content", _msg_content);
              msgNodeObj.insert("content_type", "text");
              msgNodeObj.insert("title", _msg_title);
              rootJsonObj.insert("message", msgNodeObj);
          }
      
          //
          noteJsonObj.insert("android", adrdNoteObj);
          rootJsonObj.insert("notification", noteJsonObj);
      
          jsonDoc.setObject(rootJsonObj);
          jsonArr = jsonDoc.toJson(QJsonDocument::Compact);
          qDebug() << jsonDoc;
      
          request.setHeader(QNetworkRequest::ContentLengthHeader, jsonArr.size());
          m_pNetworkAccManger->post(request, jsonArr);
      }
      
      //发送结束槽
      void QJpush::onFinished(QNetworkReply *reply)
      {
          switch (reply->error())
          {
          case QNetworkReply::NoError:
              QLOG("QNetworkReply::NoError");
              break;
          case QNetworkReply::ContentNotFoundError:
              QLOG("QNetworkReply::ContentNotFoundError");
              break;
          default:
              QString str;
              QTextStream steam(&str);
              steam << "Have error" << reply->errorString() << " error " << reply->error() << endl;
              QLOG(str);
          }
      
          delete reply;
      }
      
      //ssl错误
      void QJpush::slotSSLErrorDeal(
          QNetworkReply * reply,
          const QList<QSslError> & errors)
      {
          QList<QSslError>::const_iterator conit = errors.begin();
          while (conit != errors.end())
          {
              QString sTemp = (*conit).errorString();
              QLOG(sTemp);
              qDebug() << (*conit).errorString();
              ++conit;
          }
          reply->ignoreSslErrors();
      
          return;
      }
      QString jgPushTag("tag1/tag2");    
      QString alertStr = "异常已处理";
      QJpush jpush;
      
      jpush.pushTag(jgPushTag, alertStr, "通知", alertStr, "消息");

      编译成的项目要包含openssl的libeay32.dll和ssleay32.dll。

    4. 转载请注明出处:http://www.cnblogs.com/fyluyg/p/5703605.html

    转载于:https://www.cnblogs.com/fyluyg/p/5703605.html

    展开全文
  • Maven添加依赖 <!--极光推送--> <dependency> <groupId>cn.jpush.api</groupId> <artifactId>jpush-client</artifactId> <version>...

    Maven添加依赖

    <!--极光推送-->
            <dependency>
                <groupId>cn.jpush.api</groupId>
                <artifactId>jpush-client</artifactId>
                <version>3.2.3</version>
            </dependency>
    

    后台代码(可直接复制使用)
    工具类

    package com.rzhy.api.tjxm.util;
    
    import cn.jpush.api.push.model.Message;
    import cn.jpush.api.push.model.Options;
    import cn.jpush.api.push.model.Platform;
    import cn.jpush.api.push.model.PushPayload;
    import cn.jpush.api.push.model.audience.Audience;
    import cn.jpush.api.push.model.notification.AndroidNotification;
    import cn.jpush.api.push.model.notification.IosNotification;
    import cn.jpush.api.push.model.notification.Notification;
    
    import java.util.Map;
    
    public class JPushUtil {
        /**
         * 发送给所有用户
         * @param notificationTitle
         * @param1 avatar
         * @param1 nick
         * @param1 imTag  jumpType
         * @return
         */
        public static PushPayload buildPushObject_android_and_ios(String notificationTitle, String title, Map<String,String> map, String[]alias, boolean isApnsProduction) {
            return PushPayload.newBuilder()
                    .setPlatform(Platform.android_ios())
                    .setAudience(Audience.alias(alias))
                    .setNotification(Notification.newBuilder()
                            .setAlert(notificationTitle)
                            .addPlatformNotification(AndroidNotification.newBuilder()
                                    .setAlert(notificationTitle)
                                    //.setTitle(notification_title)
                                    //此字段为透传字段,不会显示在通知栏。用户可以通过此字段来做一些定制需求,如特定的key传要指定跳转的页面(value)
                                    //.setTitle(title)
                                    .addExtras(map)
                                    .build()
                            )
                            .addPlatformNotification(IosNotification.newBuilder()
                                    //传一个IosAlert对象,指定apns title、title、subtitle等
                                    .setAlert(notificationTitle)
                                    //直接传alert
                                    //此项是指定此推送的badge自动加1
                                    //.incrBadge(1)
                                    .disableBadge()
                                    //此字段的值default表示系统默认声音;传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒,
                                    // 如果系统没有此音频则以系统默认声音提醒;此字段如果传空字符串,iOS9及以上的系统是无声音提醒,以下的系统是默认声音
                                    .setSound("default")
                                    //此字段为透传字段,不会显示在通知栏。用户可以通过此字段来做一些定制需求,如特定的key传要指定跳转的页面(value)
                                    // .addExtra("iosNotification extras key",extrasparam)
                                    .addExtras(map)
                                    //此项说明此推送是一个background推送,想了解background看:http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
                                    // .setContentAvailable(true)
                                    .build()
                            )
                            .build()
                    )
                    .setOptions(Options.newBuilder()
                            //此字段的值是用来指定本推送要推送的apns环境,false表示开发,true表示生产;对android和自定义消息无意义
                            .setApnsProduction(isApnsProduction)
                            //此字段是给开发者自己给推送编号,方便推送者分辨推送记录
                            .setSendno(1)
                            //此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
                            .setTimeToLive(600)
                            .build()
                    )
                    .build();
        }
    
        /**
         * 透传消息
         * @param msgContent 透传消息内容
         * @param map 透传消息附加参数
         * @param alias 透传消息发送人
         * @return
         */
        public static PushPayload buildPushObject_ios_audienceMore_messageWithExtras(String msgContent, Map<String,String> map, String[] alias) {
            return PushPayload.newBuilder()
                    .setPlatform(Platform.android_ios())
                    .setAudience(Audience.alias(alias))
                    .setMessage(Message.newBuilder()
                            .setMsgContent(msgContent)
                            .addExtras(map)
                            .build())
                    .build();
        }
    
        /**
         * 发送给所有指定设备
         * @param notificationTitle
         * @param
         * @param
         * @param
         * @return
         */
        public static PushPayload buildPushObjectAllTarget(String notificationTitle, Map<String,String> map, String title, boolean isApnsProduction) {
    
            return PushPayload.newBuilder()
                    .setPlatform(Platform.android_ios())
                    .setAudience(Audience.all())
                    .setNotification(Notification.newBuilder()
                            .setAlert(notificationTitle)
                            .addPlatformNotification(AndroidNotification.newBuilder()
                                    .setAlert(notificationTitle)
                                    //.setTitle(notification_title)
                                    //此字段为透传字段,不会显示在通知栏。用户可以通过此字段来做一些定制需求,如特定的key传要指定跳转的页面(value)
                                    // .addExtra("iosNotification extras key",extrasparam)
                                    .setTitle(title)
                                    .addExtras(map)
                                    .build()
                            )
                            .addPlatformNotification(IosNotification.newBuilder()
                                    //传一个IosAlert对象,指定apns title、title、subtitle等
                                    .setAlert(notificationTitle)
                                    //直接传alert
                                    //此项是指定此推送的badge自动加1
                                    //.incrBadge(1)
                                    //此字段的值default表示系统默认声音;传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒,
                                    // 如果系统没有此音频则以系统默认声音提醒;此字段如果传空字符串,iOS9及以上的系统是无声音提醒,以下的系统是默认声音
                                    .setSound("default")
                                    //此字段为透传字段,不会显示在通知栏。用户可以通过此字段来做一些定制需求,如特定的key传要指定跳转的页面(value)
                                    // .addExtra("iosNotification extras key",extrasparam)
                                    .addExtras(map)
                                    //此项说明此推送是一个background推送,想了解background看:http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
                                    // .setContentAvailable(true)
                                    .build()
                            )
                            .build()
                    )
                    //自定义消息
                    .setMessage(Message.newBuilder()
                            .setMsgContent(notificationTitle)
                            .setTitle(title)
                            .addExtras(map)
                            .build())
                    .setOptions(Options.newBuilder()
                            //此字段的值是用来指定本推送要推送的apns环境,false表示开发,true表示生产;对android和自定义消息无意义
                            .setApnsProduction(isApnsProduction)
                            //此字段是给开发者自己给推送编号,方便推送者分辨推送记录
                            .setSendno(1)
                            //此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
                            .setTimeToLive(600)
                            .build()
                    )
                    .build();
        }
        /**
         * 根据别名推送至android
         * @param notificationTitle
         * @param alias
         * @return
         */
        public static PushPayload buildPushObjectAndroidAllAlertWithTitle(String notificationTitle, String title, String[] alias, Map<String,String> map, boolean isApnsProduction) {
            return PushPayload.newBuilder()
                    //指定要推送的平台,all代表当前应用配置了的所有平台,也可以传android等具体平台
                    .setPlatform(Platform.android())
                    //指定推送的接收对象,all代表所有人,也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
                    .setAudience(Audience.alias(alias))
                    //jpush的通知,android的由jpush直接下发,iOS的由apns服务器下发,Winphone的由mpns下发
                    .setNotification(Notification.newBuilder()
                            //指定当前推送的android通知
                            .addPlatformNotification(AndroidNotification.newBuilder()
                                    .setAlert(notificationTitle)
                                    .setTitle(title)//此字段为app名称
                                    .addExtras(map)
                                    .build())
                            .build()
                    )
                    .setOptions(Options.newBuilder()
                            //此字段的值是用来指定本推送要推送的apns环境,false表示开发,true表示生产;对android和自定义消息无意义
                            .setApnsProduction(isApnsProduction)
                            //此字段是给开发者自己给推送编号,方便推送者分辨推送记录
                            .setSendno(1)
                            //此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
                            .setTimeToLive(600)
                            .build())
                    .build();
        }
        /**
         * 根据别名推送至ios
         * @param notificationTitle
         * @param alias
         * @return
         */
        public static PushPayload buildPushObjectIosAllAlertWithTitle(String notificationTitle, String[] alias,
                                                                      Map<String,String> map, boolean isApnsProduction) {
            return PushPayload.newBuilder()
                    //指定要推送的平台,all代表当前应用配置了的所有平台,也可以传android等具体平台
                    .setPlatform(Platform.ios())
                    //指定推送的接收对象,all代表所有人,也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
                    .setAudience(Audience.alias(alias))
                    //jpush的通知,android的由jpush直接下发,iOS的由apns服务器下发,Winphone的由mpns下发
                    .setNotification(Notification.newBuilder()
                            //指定当前推送的android通知
                            .addPlatformNotification(IosNotification.newBuilder()
                                    //传一个IosAlert对象,指定apns title、title、subtitle等
                                    .setAlert(notificationTitle)
                                    .addExtras(map)
                                    //直接传alert
                                    //此项是指定此推送的badge自动加1
                                    //.incrBadge(1)
                                    //此字段的值default表示系统默认声音;传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒,
                                    // 如果系统没有此音频则以系统默认声音提醒;此字段如果传空字符串,iOS9及以上的系统是无声音提醒,以下的系统是默认声音
                                    .setSound("default")
                                    //此项说明此推送是一个background推送,想了解background看:http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
                                    // .setContentAvailable(true)
                                    .build())
                            .build()
                    )
                    .setOptions(Options.newBuilder()
                            //此字段的值是用来指定本推送要推送的apns环境,false表示开发,true表示生产;对android和自定义消息无意义
                            .setApnsProduction(isApnsProduction)
                            //此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
                            .setTimeToLive(600)
                            .build())
                    .build();
        }
    
        /**
         * 根据别名推送至指定用户
         * @param registrationId
         * @param notificationTitle
         * @param
         * @param
         * @param
         * @return
         */
        public static PushPayload buildPushObjectAllRegistrationIdAlertWithTitle(String registrationId,
                                                                                 String notificationTitle, String title,
                                                                                 Map<String,String> map, boolean isApnsProduction) {
            //创建一个IosAlert对象,可指定APNs的alert、title等字段
            return PushPayload.newBuilder()
                    //指定要推送的平台,all代表当前应用配置了的所有平台,也可以传android等具体平台
                    .setPlatform(Platform.all())
                    //指定推送的接收对象,all代表所有人,也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
                    .setAudience(Audience.alias(registrationId))
                    //jpush的通知,android的由jpush直接下发,iOS的由apns服务器下发,Winphone的由mpns下发
                    .setNotification(Notification.newBuilder()
                            //指定当前推送的android通知
                            .addPlatformNotification(AndroidNotification.newBuilder()
                                    .setAlert(notificationTitle)
                                    .setTitle(title)
                                    //此字段为透传字段,不会显示在通知栏。用户可以通过此字段来做一些定制需求,如特定的key传要指定跳转的页面(value)
                                    // .addExtra("iosNotification extras key",extrasparam)
                                    .addExtras(map)
                                    .build())
                            //指定当前推送的iOS通知
                            .addPlatformNotification(IosNotification.newBuilder()
                                    //传一个IosAlert对象,指定apns title、title、subtitle等
                                    .setAlert(notificationTitle)
                                    //此字段为透传字段,不会显示在通知栏。用户可以通过此字段来做一些定制需求,如特定的key传要指定跳转的页面(value)
                                    // .addExtra("iosNotification extras key",extrasparam)
                                    .addExtras(map)
                                    //.incrBadge(1)
                                    .setSound("default")
                                    .build())
                            .build())
                    .setOptions(Options.newBuilder()
                            //此字段的值是用来指定本推送要推送的apns环境,false表示开发,true表示生产;对android和自定义消息无意义
                            .setApnsProduction(isApnsProduction)
                            //此字段是给开发者自己给推送编号,方便推送者分辨推送记录
                            .setSendno(1)
                            //此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天;
                            .setTimeToLive(600)
                            .build())
                    .build();
        }
    }
    
    

    AuroraPusher类
    填写极光appKey 、masterSecret ,根据实际调用android,ios推送

    **package com.rzhy.api.tjxm.util;
    
    import cn.jpush.api.JPushClient;
    import cn.jpush.api.common.resp.APIConnectionException;
    import cn.jpush.api.common.resp.APIRequestException;
    import cn.jpush.api.push.PushResult;
    import cn.jpush.api.push.model.PushPayload;
    import org.apache.commons.lang3.ArrayUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    public class AuroraPusher {
        public final static Logger logger = LogManager.getLogger(AuroraPusher.class);
    
    	
        private String masterSecret = "";
    
    	
        private String appKey = "";
    
        /*
        此字段的值是用来指定本推送要推送的apns环境,false表示开发,true表示生产;对android和自定义消息无意义
        */
        private boolean isApnsProduction = false;
    
        /** 第三方规定推送接收对象数量 */
        private final int aliaSize = 38;
    
        /** 推送客户端 */
        private JPushClient jPushClient;
    
        /** 获取推送客户端 */
        public JPushClient getjPushClient() {
            if(jPushClient == null){
                jPushClient = new JPushClient(masterSecret, appKey);
            }
            return jPushClient;
        }
    
        /**
         * 发送推送
         * @param payload 推送内容
         * @return
         */
        private boolean push(PushPayload payload) throws Exception{
            try {
                PushResult pushResult = this.getjPushClient().sendPush(payload) ;
                logger.debug("调用推送方法响应结果为:" + pushResult != null ? pushResult.toString() : "推送响应内容为空");
    
                /*final int code = 200;
                //存在有未登录用户错误码
                final int error_code = 1011;
    
                System.out.println(pushResult.sendno);
                if(pushResult == null || code != pushResult.sendno){
                    if(error_code == pushResult.sendno){
                        throw new SendMessageFailureException(error_code, "选择用户中包含存未在APP登录激活的用户");
                    }
                    throw new SendMessageFailureException("推送消息时,第三方响应错误,错误原因为:");
                }*/
            } catch (APIConnectionException e) {
                logger.error("推送消息时,连接第三方异常, 异常原因:" + e.getMessage(), e);
                throw new SendMessageFailureException("推送消息时,连接第三方异常");
            } catch (APIRequestException e) {
                logger.error("推送消息时,请求第三方异常, 异常原因:" + e.getMessage(), e);
                throw new SendMessageFailureException("推送消息时,请求第三方异常");
            }
            return true;
        }
    
        /**
         * 推送至所有设备
         * @param notificationTitle
         * @param title
         * @param map
         * @return
         */
        public boolean pushAllTarget(String notificationTitle, String title, Map<String, String> map) throws Exception{
            PushPayload payload = JPushUtil.buildPushObjectAllTarget(notificationTitle, map, title, isApnsProduction);
            return  this.push(payload);
        }
    
        /**
         * 推送至所有设备
         * @param notificationTitle
         * @param map
         * @param title
         * @param alias
         * @return
         */
        public boolean pushAllTargetByUser(String notificationTitle, String title, Map<String, String> map, String[] alias) throws Exception{
            PushPayload payload;
            List<List<String>> aliaList = splitArraySpecifySizeList(alias, aliaSize);
            for(List<String> itemList : aliaList){
                payload = JPushUtil.buildPushObject_android_and_ios(notificationTitle, title, map, itemList.toArray(new String[]{}), isApnsProduction);
                this.push(payload);
            }
            return true;
        }
    
        /**
         * 透传消息
         * @param msgContent 透传消息内容
         * @param map 附加信息
         * @param alias 接收人
         * @return
         */
        public boolean buildPushObjectAudienceMoreMessageWithExtras(String msgContent, Map<String,String> map, String[] alias)throws Exception{
            PushPayload payload;
            List<List<String>> aliaList = splitArraySpecifySizeList(alias, aliaSize);
            for(List<String> itemList : aliaList){
                payload = JPushUtil.buildPushObject_ios_audienceMore_messageWithExtras(msgContent, map, itemList.toArray(new String[]{}));
                this.push(payload);
            }
            return true;
        }
    
        /**
         * 根据ilias推送至android
         * @param msg
         * @param title
         * @param alias
         * @param map
         * @return
         */
        public boolean sendAllAndroid(String msg, String title, String[] alias, Map<String, String> map) throws Exception{
            PushPayload payload;
            List<List<String>> aliaList = splitArraySpecifySizeList(alias, aliaSize);
            for(List<String> itemList : aliaList){
                payload = JPushUtil.buildPushObjectAndroidAllAlertWithTitle(msg, title, itemList.toArray(new String[]{}), map, isApnsProduction);;
                this.push(payload);
            }
            return true;
        }
    
        /**
         * 根据ilias推送至ios
         * @param msg
         * @param alias
         * @param map
         * @return
         */
        public boolean sendAllIos(String msg, String[] alias, Map<String, String> map) throws Exception {
            PushPayload payload;
            List<List<String>> aliaList = splitArraySpecifySizeList(alias, aliaSize);
            for(List<String> itemList : aliaList){
                payload = JPushUtil.buildPushObjectIosAllAlertWithTitle(msg, itemList.toArray(new String[]{}), map, isApnsProduction);
                this.push(payload);
            }
            return true;
        }
    
        /**
         * 根据alias推送至指定设备
         * @param registrationId
         * @param notificationTitle
         * @param title
         * @param map
         * @return
         */
        public boolean sendByAlias(String registrationId, String notificationTitle, String title, Map<String, String> map) throws Exception{
            PushPayload payload;
            System.out.println(registrationId);
            String[] alias = registrationId.split(",");
            if(alias.length < aliaSize){
                payload = JPushUtil.buildPushObjectAllRegistrationIdAlertWithTitle(registrationId, title, notificationTitle, map, isApnsProduction);
                System.out.println(payload);
                this.push(payload);
            } else {
                List<List<String>> list = splitArraySpecifySizeList(alias, aliaSize);
                for(List<String> temp : list){
                    payload = JPushUtil.buildPushObjectAllRegistrationIdAlertWithTitle(String.join(",", temp), title, notificationTitle, map, isApnsProduction);
                    this.push(payload);
                }
            }
            return true;
        }
    
        /**
         * 拆分接收对象为指定数量的数组对象集合
         * @param alias 推送接收对象
         * @param size 指定数量
         * @return
         */
        private List<List<String>> splitArraySpecifySizeList(String[] alias, final int size){
            if(ArrayUtils.isEmpty(alias)){
                return null;
            }
            //将数组转成集合 固定长度
            List<String> list = Arrays.asList(alias);
            return getSubList(list,size);
        }
    
    
        /**
         * 将一个List集合拆分成指定长度的List
         *
         * @param list 母list
         * @param len 子list的长度
         * @return resultList 结果List<List>
         */
        private static List<List<String>> getSubList(List list,int len) {
            if (list == null || list.size() == 0 || len < 1) {
                return null;
            }
            List<List<String>> resultList = new ArrayList<>();
            for (int i = 0; i < list.size(); i++) {
                if ( i % len == 0 ) {
                    int count = i/len;
                    List subList = (List) list.stream().limit((count + 1) * len).skip(count * len).collect(Collectors.toList());
                    resultList.add(subList);
                }
            }
            return resultList;
        }
    }
    **
    

    调用极光推送 (此处是将token当作别名推送消息)

    @Component
    public class WeiXinUtil {
        @Autowired
        private  INoticeuserService noticeuserService;
    	//静态的方法调用service接口
        private static WeiXinUtil weiXinUtil;
    
        //重点三:初始化
        @PostConstruct
        public void init() {
            weiXinUtil= this;
            weiXinUtil.noticeuserService= this.noticeuserService;
        }
        
    	//体检预约消息推送
        public static void sendNewYyjl(Yyjl t) throws Exception{
            // 先发送app终端用户
            sendNewYyjlPush(t,1);
                // 发送客服
            sendNewYyjlPush(t,2);
            //保存消息推送记录
            Noticeuser noticeuser = new Noticeuser();
            noticeuser.setNotice(1);
            noticeuser.setTitle("您好,您已经成功在国际保健预约体检。");
            noticeuser.setNoticevalue("预约通知");
            noticeuser.setName(t.getName());
            noticeuser.setToken(t.getToken());
            noticeuser.setMobile(t.getPhone());
            System.out.println(noticeuser);
    
            weiXinUtil.noticeuserService.updateNoticeuser(noticeuser);
    
        }
        //预约体检消息推送
        public static void sendNewYyjlPush(Yyjl t,Integer sendtype) throws  Exception {
    
            //先发送给用户
            String notice = "";
            String title = "";
            String alert = "";
            if (t.getKhtype() == 1){  //判断是个人还是公司预约
                if (sendtype == 1) {
                    notice = "您好,您已经成功在国际保健预约体检,请及时登录查看。";
                    title = "您好,您已经成功在国际保健预约体检。";
                    alert = "预约类型:个人体检\\n联系方式:" + t.getPhone()
                            + "\\n预约日期" + t.getYydate()
                            + "\\n如有任何问题请致电83009000进行咨询";
                } else {
                    title = "有一个新的个人体检预约。";
                    alert = "请及时登录后台查看。";
                }
            } else {
                if (sendtype == 1) {
                    notice = "您好,您已经成功在国际保健预约体检,请及时登录查看。";
                    title = "您好,贵公司已经成功在国际保健预约体检。";
                    alert ="预约类型:公司体检\\n公司名称:" + t.getCompany()
                            + "\\n体检人数" + t.getNumber()
                            + "\\n预约日期" + t.getYydate()
                            + "\\n如有任何问题请致电83009000进行咨询";
                } else {
                    title = "有一个新的个人体检预约。";
                    alert = "请及时登录后台查看。";
                }
            }
            Map<String,String> map = new HashMap<>();
            map.put("title",title);
            map.put("alert",alert);
            AuroraPusher auroraPusher = new AuroraPusher();
            auroraPusher.sendByAlias(t.getToken(),notice,title,map);
        }
    
    展开全文
  • 想实现一个简单的功能,服务器向APP发送一个hello的消息APP在没有登录的情况下也能收到,网上说的都是利用第三方平台实现推送,我想自己写个服务器实现
  • app推送消息通知

    2015-12-30 10:06:01
    ...PHP推送模板实例 点击通知打开应用模板(PHP) 点击通知打开网页模板(PHP) 点击通知栏弹框下载模版(PHP) 透传消息模板(PHP)
  • 如何实现向APP推送消息

    千次阅读 2016-06-14 11:28:06
    推送的基本原理其实类似,其实就是通过手机和服务器之间的Socket维持一个TCP长连接,通过这个长连接来实现服务器和客户端之间的通信。Socket编程实现起来比较复杂,可以利用第三方推送sdk,也有大量的开源技术可以...
  • java后台对app推送消息

    2015-05-05 06:41:58
    公司现在是做代驾软件的,有一个客户端app和一个司机端app,后台专门写了一个客户端提交订单的接口,客户提交代驾订单以后后台接收订单信息,然后计算最近的司机,并将订单信息通过第三方推送“个推”直接推送给这个...
  • APP推送消息测试点分析

    千次阅读 2019-07-31 11:07:36
    APP推送消息测试点分析 1、虽然以前学测试的时候接触一点消息推送测试点,但是自己理解和掌握的测试点还不够深,由于工作需求,这两天深入了解和学了一下APP推送消息是什么,怎么测的问题 设计好的推送消息是APP提升...
  • 出现的问题:PC端已经处理好的消息推送出现到APP端。 问题原因:这是由于推送的流程问题引起的,流程如下: 数据库--服务器(未判断消息)--第三方推送平台--APP 这种是由于APP直接从服务器端接受消息条数提醒,而...
  • 这两天公司业务有需求,当用户进行一些操作,比如下订单、支付等操作时,需要关联后台自动向app推送顶栏消息。为了实现这个功能,使用了腾讯信鸽推送。《腾讯信鸽推送》已经封装好了推送代码,只需要调用它一个方法...
  • Android爬取第三方app推送消息

    千次阅读 2017-12-31 23:48:23
    Android利用NotificationListenerService获取第三方APP通知所携带的Intent以及Intent所携带的参数。是Intent的参数哦,不是通知的参数哦
  • 最近做的一个项目需要用到服务器向app远程推送消息,百度到的教程大部分都是一两年前写的,由于教程版本的古老,中间遇到各种坑,导致这个功能拖了几个星期才搞定,今日终于脱坑,记录一下自己的操作步骤,填一些坑...
  • 问题描述:我在PC端做一个操作;操作完成之后,就在APP端提醒我进行了操作 实现这种功能请问大家有没有什么参考的
  • 金九银十,给大家整理了月薪20K的Android面试题必问集锦,希望让大家查漏补缺,最后祝大家都能在金九银十找到一份不错的工作! 二、理解架构 全球有名的架构目前分为四种: 1、Clean架构:一种分层的架构方式,将...
  • 如题,求大神帮忙解答
  • 直接上效果图抓去状态栏消息ActivityNotificationMonitorServiceAndroidManifest.xml不想敲代码就复制下方代码Androdi代码自定义NotificationMonitorService 继承NotificationListenerServiceimport android.app....
  • 开头 金九银十就快到了,很多有求职、跳槽打算的人最近都在完善更新自己的简历,打算趁此机会换到心仪的环境。 程序员相较其它工作岗位略有不同,最注重的就是技术。所以很多程序员会产生一个误区,觉得自己技术强,...
  • 代码重构能力,推荐书籍:《重构改善既有代码的设计》、《重构与模式》 Google 动态化框架 App Bundles。 Jetpack,Google 推出的新一代组件、工具和架构指导,旨在加快 Android 应用开发速度。Jetpack 主要分为 4 ...
  • 给你的Android APP推送消息可以使用GCM,不过如果有办法让app知道自己应该何时连接推送provider server,则没必要使用GCM了。 Google Cloud Messaging for Android Google Cloud Messaging for ...
  • 后台是.NET的,通过webapi让app取数据,现在想做推送(而不是app来轮询),搜了半天没靠谱的方法,大部分是没用的信息,还有部分看似有用,但都是说的用第三方服务(比如啥“极光推送”)来实现。。。。 想问问...
  • 四、 Android中的事件处理 1、 Handler机制 2、 事件分发机制 3、 子线程发消息到主线程进行更新UI,除了handler和AsyncTask,还有什么? 4、 子线程中能不能new handler?为什么? 五、 Android中的动画 1、 ...
  • iOS 开发之实现 App 消息推送(最新)

    万次阅读 多人点赞 2014-11-19 21:13:10
    今天就由本菜鸟给大家做一个简单的IOSApp消息推送教程吧!一切从0开始,包括XCode6, IOS8, 以及苹果开发者中心最新如何注册应用,申请证书以及下载配置概要文件,相信很多刚开始接触ios的人会很想了解一下。(ps:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,502
精华内容 1,400
关键字:

app推送消息