Remember工具 半成品

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 前言Mac上有一个自带的备忘录,感觉还挺好用的。然后也想自己动手,做个类似的Remember小工具来玩一下。工具类型:胖服务端,瘦客户端的模式。大致的场景就是客户端只管把自己想让被提醒的事项发给服务器端,然后配合自己的本地扫描,对符合要求的memo进行弹框提醒。

禅心 · 悟道

前言

Mac上有一个自带的备忘录,感觉还挺好用的。然后也想自己动手,做个类似的Remember小工具来玩一下。

工具类型:胖服务端,瘦客户端的模式。大致的场景就是客户端只管把自己想让被提醒的事项发给服务器端,然后配合自己的本地扫描,对符合要求的memo进行弹框提醒。

最近对Redis比较着迷一点,被其优雅高效的设计所打动。虽然对于搜索方面支持的不太好,但是搜索的话使用专业的搜索服务就好了。我个人比较崇尚Unix工具系的宗旨:一个工具只专注于做一件事,这也是Redis目前所体现的。

服务器端

对于服务器端的设计的初衷,是一个“胖胖的”,做了大部分的工作的形式,但是做着做着,发现客户端做的工作其实也蛮多的。目前服务器端的任务是:
- 接受用户注册
- 对memo支持CRUD
- 对请求进行“安全甄选”

目录结构

  server ll *.py
-rw-r--r--  1 changba164  staff   346B Oct 28 14:53 datastructor.py # 常见数据结构bean
-rw-r--r--  1 changba164  staff   2.6K Oct 28 18:22 redishelper.py  # redis操作相关的工具类
-rw-r--r--  1 changba164  staff   4.6K Oct 28 18:42 server.py   # 对客户端提供服务支持

代码比较简单,下面简单来看下具体的内容。

datastructor.py

#!/usr/bin python
# coding: utf8
# file: .py

import sys

reload(sys)
sys.setdefaultencoding('utf8')
import json

class ResponseCode(object):
    """
    服务响应码
    """
    def __init__(self, code, msg):
        self.code = code
        self.msg = msg

    def getcode(self):
        return json.dumps({"code":self.code, "msg":self.msg})

redishelper.py

#!/usr/bin python
# coding: utf8
# file: .py

import sys

reload(sys)
sys.setdefaultencoding('utf8')

import redis
import hashlib
import uuid
import time

class RedisHelper(object):
    """
    redis操作相关的工具类。
    """
    def __init__(self):
        self.rs = redis.Redis(host="localhost", port=6379, db=3, encoding="utf8", charset="utf8")
        # 相关key
        self.uids = "userids:set"
        self.rank = "unfinished:zset:"
        self.unfinished = "unfinished:hash:"
        self.finished = "finished:hash:"

    def check_request_valid(self, uid, securitycode):
        if uid is None:
            return False
        if securitycode == hashlib.md5(uid).hexdigest():
            return True
        return False

    def register(self, uid):
        if uid is None:
            return False
        self.rs.sadd(self.uids, uid)
        return True

    def check_user(self, uid):
        if uid is None:
            return False
        return True if self.rs.sismember(self.uids, uid) else False

    def add_memo(self, uid, memoid, memo=""):
        if uid is None:
            return False
        memoid = memoid
        self.rs.sadd(str(uid), memoid)
        self.rs.hset(self.unfinished+str(uid), memoid, memo)
        self.rs.zadd(self.rank+str(uid), memoid, int(memoid))


    def update_memo(self, uid, memoid, memo):
        if uid is None:
            return False
        if not self.rs.sismember(str(uid), memoid):
            return False
        self.rs.hset(self.unfinished+str(uid), memoid, memo)
        return True

    def delete_memo(self, uid, memoid):
        if uid is None:
            return False
        memo = self.rs.hget(self.unfinished+str(uid), memoid)
        self.rs.hset(self.finished+str(uid), memoid, memo)
        self.rs.zrem(self.rank+str(uid), memoid)
        return True

    def get_memo(self, uid, memoid):
        if uid is None:
            return None
        return self.rs.hget(self.unfinished+str(uid), memoid)

    def get_memos(self, uid, reverse=True):
        if uid is None:
            return None
        memoids = self.get_memoids(uid, reverse)
        print memoids
        memos = []
        for item in memoids:
            memos.append(self.rs.hget(self.unfinished+str(uid), item[0]))
        return memos

    def get_memoids(self, uid, reverse=True):
        if uid is None:
            return []
        if reverse == True:
            return [item[0] for item in self.rs.zrevrange(self.rank+str(uid), 0, -1, withscores=True)]
        else:
            return [item[0] for item in self.rs.zrange(self.rank+str(uid), 0, -1, withscores=True)]

server.py

#!/usr/bin python
# coding: utf8
# file: .py

import sys

reload(sys)
sys.setdefaultencoding('utf8')
from flask import Flask, request
import redishelper
import datastructor
import logging
import json

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s:%(levelname)s %(message)s',
    datafmt="%Y-%m-%d %H:%i:%s",
    filename="/Users/changba164/guo/tools/remeber/server/memo-server.log",
    filemode="a"
)

app = Flask(__name__)
helper = redishelper.RedisHelper()


@app.route("/", methods=['GET', 'POST'])
def home():
    return "It works!"

@app.route("/register", methods=["POST"])
def register():
    uid = request.form["uid"]
    helper.register(uid)
    res = datastructor.ResponseCode(1000, "uid={}".format(uid))
    return res.getcode()

@app.route("/add", methods=["POST"])
def add():
    uid = request.form['uid']
    securitycode = request.form['securitycode']
    memoid = request.form['memoid']
    memo = request.form['memo']

    isuserexists = helper.check_user(uid)
    if isuserexists == False:
        return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
    isvalid = helper.check_request_valid(uid, securitycode)
    logging.info("{}: {}".format(uid, securitycode))
    if isvalid == False:
        return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
    helper.add_memo(uid, memoid, memo)
    return datastructor.ResponseCode(1000, "memo已经保存啦!").getcode()

@app.route("/update", methods=["POST"])
def update():
    uid = request.form['uid']
    securitycode = request.form['securitycode']
    memoid = request.form['memoid']
    memo = request.form['memo']

    isuserexists = helper.check_user(uid)
    if isuserexists == False:
        return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
    isvalid = helper.check_request_valid(uid, securitycode)
    if isvalid == False:
        return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
    helper.update_memo(uid, memoid, memo)
    return datastructor.ResponseCode(1000, "memo已经更新啦!").getcode()

@app.route("/delete", methods=["POST"])
def deletememo():
    uid = request.form['uid']
    securitycode = request.form['securitycode']
    memoid = request.form['memoid']

    isuserexists = helper.check_user(uid)
    if isuserexists == False:
        return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
    isvalid = helper.check_request_valid(uid, securitycode)
    if isvalid == False:
        return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
    helper.delete_memo(uid, memoid)
    return datastructor.ResponseCode(1000, "memo已经删除/完成啦!").getcode()

@app.route("/detail", methods=["POST"])
def detail():
    uid = request.form['uid']
    securitycode = request.form['securitycode']
    memoid = request.form['memoid']

    isuserexists = helper.check_user(uid)
    if isuserexists == False:
        return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
    isvalid = helper.check_request_valid(uid, securitycode)
    if isvalid == False:
        return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
    detail = helper.get_memo(uid, memoid)
    return datastructor.ResponseCode(1000, detail).getcode()

@app.route("/lists", methods=['POST'])
def lists():
    uid = request.form['uid']
    securitycode = request.form['securitycode']
    reverse = request.form['reverse']
    isuserexists = helper.check_user(uid)
    if isuserexists == False:
        return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
    isvalid = helper.check_request_valid(uid, securitycode)
    if isvalid == False:
        return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
    memos = helper.get_memos(uid, reverse)
    res = datastructor.ResponseCode(1000, json.dumps((memos))).getcode()
    return res

@app.route("/recent", methods=['POST'])
def recentids():
    uid = request.form['uid']
    securitycode = request.form['securitycode']
    isuserexists = helper.check_user(uid)
    if isuserexists == False:
        return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
    isvalid = helper.check_request_valid(uid, securitycode)
    if isvalid == False:
        return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
    memoid = helper.get_memoids(uid, False)[0]
    result = {
        "memoid": memoid,
        "memo": helper.get_memo(uid, memoid)
    }
    res = datastructor.ResponseCode(1000, json.dumps((result))).getcode()
    return res


if __name__ == '__main__':
    app.run(host='localhost', port=9999, debug=True)

“瘦客户端”

客户端所要做的无非是:新增memo,获得最需要处理的memo,并进行弹窗提示等。目前客户端的任务还不算完成,也就简单实现了下。

  client ls
testutils.py utils.py
  client

utils.py

#!/usr/bin python
# coding: utf8
# file: .py

import sys

reload(sys)
sys.setdefaultencoding('utf8')

import requests
import json
import hashlib
import time
import datetime
import tkMessageBox
from Tkinter import *

def get_security_code(uid):
    return hashlib.md5(uid).hexdigest()

def time_to_stamp(seed):
    """
    将白话点的时间转换为时间戳,格式为: 00:00:00:00 天:小时:分钟:秒
    """
    curtime = int(time.time())
    d, h, m, s = (int(item) for item in str(seed).split(":"))
    targettime = curtime + (d*86400)+ (h*3600)+(m*60)+s
    return targettime

def stamp_to_time(stamp):
    return datetime.datetime.utcfromtimestamp(float(stamp))

def make_dialog(timestr, msg):
    tkMessageBox.showinfo(title=timestr, message=msg)
    # root = Tk()
    # root.title(timestr)
    # frame = Frame(root)
    # Label(frame, text=msg).pack()
    # frame.pack(side=TOP)
    # root.mainloop()

class UserService(object):
    """
    注册用户
    """
    def __init__(self):
        pass

    @staticmethod
    def register(uid):
        if uid is None:
            return False
        url = "http://localhost:9999/register"
        response = requests.post(url, data={"uid": uid})
        if response.status_code == 200 and response.json()['code'] == 1000:
            return True
        return False




class MemoHelper(object):
    """
    客户端memo工具类
    """
    def __init__(self):
        self.url = "http://localhost:9999/{}"

    def post(self, uid, memoid, memo):
        posturl = self.url.format("add")
        payload = {
            "uid": uid,
            "securitycode": get_security_code(uid),
            "memoid": memoid,
            "memo": memo
        }
        response = requests.post(posturl, data=payload)
        return response.text
    def getmemo(self, uid, memoid):
        url = self.url.format("detail")
        payload = {
            "uid": uid,
            "securitycode": get_security_code(uid),
            "memoid": memoid
        }
        response = requests.post(url, data=payload)
        return response.text
    def getmemos(self, uid, reverse=True):
        url = self.url.format("lists")
        payload = {
            "uid": uid,
            "securitycode": get_security_code(uid),
            "reverse": reverse
        }
        response = requests.post(url, data=payload)
        return response.text
    def getrecent(self, uid):
        url = self.url.format("recent")
        payload = {
            "uid": uid,
            "securitycode": get_security_code(uid)
        }
        response = requests.post(url, data=payload)
        return response.text
    def updatememo(self, uid, memoid, memo):
        url = self.url.format("update")
        payload = {
            "uid": uid,
            "securitycode": get_security_code(uid),
            "memoid": memoid,
            "memo": memo
        }
        response = requests.post(url, data=payload)
        return response.text
    def deletememo(self, uid, memoid):
        url = self.url.format("delete")
        payload = {
            "uid": uid,
            "securitycode": get_security_code(uid),
            "memoid": memoid
        }
        response = requests.post(url, data=payload)
        return response.text


class Emitter(object):
    """
    检测到到期任务,则弹出一个提醒框!
    """
    def __init__(self, uid):
        self.uid = str(uid)
        self.memohelper = MemoHelper()

    def emit_in(self, timestr, memo):
        timestamp = time_to_stamp(timestr)
        self.memohelper.post(self.uid, timestamp, memo)



    def emit_out(self):
        # 如果时间符合要求就emit出来, 找出距离时间最远的memo。
        data = self.memohelper.getrecent(self.uid)
        data = json.loads(data)
        data = data['msg']
        data = json.loads(data)
        targettime = stamp_to_time(data['memoid'])
        memo = data['memo']
        make_dialog(targettime, memo)


if __name__ == '__main__':

    emitter = Emitter(201393260)
    emitter.emit_out()

总结

总体来说,这个小工具没有达到我的预期效果,大致有如下几点:
- 扫描服务可以说没怎么做。
- “安全”这里简单就是限制客户端请求方式为POST,简单的使用securitycode做了下判断(如果被抓包分析的话,那也没什么用,比较好的做法就是模仿Flask使用PIN码,来进一步提高安全性)。
- 客户端弹框效果不是很理想,而且对于发布一个POST而言,还没做对应的GUI支持,目前也就在命令行里面使用类似:

emitin xxxxxxx a:b:c:d
大致的意思是: 记录 在a天b小时c分钟d秒 后要做的xxxxxxxx事项。

有兴趣的可以在目前的代码基础上进行优化,这里权当抛砖引玉吧。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
6月前
|
敏捷开发 项目管理
什么是敏捷开发项目管理应用 JIRA 里 User Story 的 Story Point 字段
什么是敏捷开发项目管理应用 JIRA 里 User Story 的 Story Point 字段
44 0
|
6月前
|
敏捷开发
敏捷开发领域里的 Epic 以及和 User Story 的关联关系
敏捷开发领域里的 Epic 以及和 User Story 的关联关系
81 2
jira学习案例84-应用redux-tookit管理框
jira学习案例84-应用redux-tookit管理框
49 0
jira学习案例84-应用redux-tookit管理框
|
安全 Java 数据库
Remember 功能源码跟踪|学习笔记
快速学习 Remember 功能源码跟踪
71 0
Remember 功能源码跟踪|学习笔记
SAP MM在ML81N事务代码界面报错- Customizing incorrectly maintained –之对策
SAP MM在ML81N事务代码界面报错- Customizing incorrectly maintained –之对策
SAP MM在ML81N事务代码界面报错- Customizing incorrectly maintained –之对策
|
前端开发
SAP HUM 内向交货单凭证流和Relationship Browser
SAP HUM 内向交货单凭证流和Relationship Browser
SAP HUM 内向交货单凭证流和Relationship Browser
|
测试技术
SAP Spartacus 电商云 UI Shipping Method 在单元测试环境下没有显示的问题
SAP Spartacus 电商云 UI Shipping Method 在单元测试环境下没有显示的问题
122 0
SAP Spartacus 电商云 UI Shipping Method 在单元测试环境下没有显示的问题
Opportunity在Fiori cloud system上编辑之后超时的根源分析
Opportunity在Fiori cloud system上编辑之后超时的根源分析
Opportunity在Fiori cloud system上编辑之后超时的根源分析
关于用 ABAP 代码手动触发 SAP CRM organization Model 自动决定的研究
关于用 ABAP 代码手动触发 SAP CRM organization Model 自动决定的研究
105 0
关于用 ABAP 代码手动触发 SAP CRM organization Model 自动决定的研究
|
存储 区块链 开发工具
如何在基于Bytom开发过程中使用Bigchaindb
上期我们讲了在基于比原开发过程中链外存储可以用分布式存储IPFS,这期我们还给大家介绍另外一种链外存储的解决方案。bigchaindb:https://www.bigchaindb.com,下面我们讲一下集成过程。
1454 0