可扩展自动消息检测提醒系统

解决方案

  • 通过任务队列实现自动化
  • 通过对任务定制实现消息收发
  • 通过APIGitHub实现扩展

前端 Vue.js

 

1. Index实现主页/登录

2. Register注册页

3. Manage用户任务管理

4. Log任务日志查看

 

import Vue from 'vue';
import Router from 'vue-router';
import Register from '@/components/Register';
import Index from '@/components/Index';
import Manage from '@/components/Manage';
import Log from '@/components/Log';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Index',
      component: Index,
    },
    {
      path: '/register',
      name: 'Register',
      component: Register,
    },
    {
      path: '/manage',
      name: 'Manage',
      component: Manage,
    },
    {
      path: '/log/:id',
      name: 'Log',
      component: Log,
    },
  ],
});

文件结构

router 控制路由、页面跳转

 

模块化components

 

store存储了页面状态

Index

export default {
  name: 'index',
  components: {
    Particles,
    Navbar,
    Panel,
    FunctionsStatus,
  },
  data() {
    return {
    };
  },
};

Register

  mounted: function mounted() {...},
  watch: {
    username() {...},
    email() {...},
    code() {...},
    password() {...},
  },
  methods: {
    ok() {...},
    visiableChange(visiable) {...},
    register() {...},
    registerStatus() {...},
    usernameTest(only) {...},
    passwordTest() {...},
    emailTest(only) {...},
    codeTest(only) {...},
  },
  computed: {
    usernameFeedback() {...},
    usernameState() {...},
    passwordFeedback() {...},
    passwordState() {...},
    emailFeedback() {...},
    emailState() {...},
    codeFeedback() {...},
    codeState() {...},
  },

Manage

methods: {
  visiableChange(visiable) {...},
  generateParams(ajaxURL) {...},
  initChangeForm(id) {...},
  initForm() {...},
  submitChange(e) {...},
  submitCreate(e) {...},
  taskDelete(id) {...},
  getFunctions() {...},
  getFuncForm(id) {...},
  updateTableState() {...},
  getTask() {...},
watch: {
  createFunc: function createFunc(newFuncID) {...},
  items: function items() {...},
},
mounted: function mounted() {...},

Log

getLog() {
  Axios({
    method: 'get',
    url: `/api/core/task/log/${this.$route.params.id}/`,
  })
  .then((response) => {
    if (response.data.status === 213) {
      this.logs = response.data.data;
      this.$set(this, 'toastr', {
        message: response.data.message,
        title: 'Loaded',
        type: 'success',
      });
    } else {
      this.$set(this, 'toastr', {
        message: response.data.message,
        title: 'error',
        type: 'warning',
      });
    }
    this.$set(this, 'visiable', true);
  })
  .catch((response) => {
    this.$set(this, 'toastr', {
      message: response.data,
      title: 'Server error, please contact server manager',
      type: 'error',
    });
    this.$set(this, 'visiable', true);
  });
},

后端 Django

 

1. RESTfulAPI

2. 核心框架send_core

3. 扩展APP

 

from django.conf.urls import url
from send_core import views

urlpatterns = [
    url(r'^$', views.index),
    url(r'^get/functions/$', views.get_functions),
    url(r'^user/whoami/$', views.whoami),
    url(r'^user/login/$', views.login),
    url(r'^user/logout/$', views.logout),
    url(r'^user/register/$', views.register),
    url(r'^get/status/(?P<target>\w+)/$', views.status),
    url(r'^get/function/form/(?P<target>\d+)/$', views.get_function_form),
    url(r'^user/username/valid/$', views.username_valid),
    url(r'^user/email/valid/$', views.email_valid),
    url(r'^user/code/valid/$', views.code_valid),
    url(r'^user/task/$', views.task),
    url(r'^task/create/$', views.task_create),
    url(r'^task/detail/(?P<target>\d+)/$', views.task_detail),
    url(r'^task/change/$', views.task_change),
    url(r'^task/delete/(?P<target>\d+)/$', views.task_delete),
    url(r'^task/log/(?P<target>\d+)/$', views.task_log),
]

eg: 用户修改任务参数(API)

def task_change(request):
    response = {
        'status': -1,
        'message': 'error',
    }
    if request.user.is_authenticated():
        raw_data = json.loads(request.body.decode())
        if raw_data['taskID'] <= 0:     # check it is cause by change.
            response['status'] = 452
            response['message'] = 'taskID error'
        else:
            tasks = Task.objects.filter(id=raw_data['taskID'])
            if len(tasks) == 1:
                task = tasks[0]
                if task.person == request.user:     # check belongs
                    task.params = json.dumps({'input': raw_data['input']}, ensure_ascii=False)
                    task.comment = raw_data['input'][-1]['value']
                    task.check = 0
                    task.status = '重新创建'
                    task.save()
                    response['status'] = 211
                    response['message'] = 'change success, task id is %d' % raw_data['taskID']
                else:
                    response['status'] = 451
                    response['message'] = 'This task is not belongs to you'
    return HttpResponse(json.dumps(response))

eg: Task表结构(django.db.models)

class Task(models.Model):
    person = models.ForeignKey(User, related_name='person_task', db_index=True)
    function = models.ForeignKey(Function, related_name='function_task', db_index=True)
    comment = models.CharField(max_length=128, default='')
    status = models.CharField(max_length=128, default='')
    success = models.IntegerField(default=0)
    failed = models.IntegerField(default=0)
    last_exec = models.DateTimeField(blank=True)
    next_exec = models.DateTimeField(blank=True)
    params = models.CharField(max_length=2048, default='{}')    # json.dumps
    check = models.IntegerField(default=0)

    def __str__(self):
        return '[%d]%s start %s, %s, next is %s' % (self.id, self.person.username, 
                self.function.name, self.status, self.next_exec.strftime('%Y-%m-%d %H:%M:%S'))

    class Meta:
        ordering = ['id', 'person', 'function']      

任务调度逻辑

1. Celery发现任务

2. task.py包含任务

3. 任务结束后发出消息

app = Celery('send_core', backend='amqp://guest@127.0.0.1//', broker='amqp://guest@127.0.0.1//')
app.conf.update(
    CELERY_ACCEPT_CONTENT = ['application/json'],
    CELERY_TASK_SERIALIZER = 'json',
    CELERY_RESULT_SERIALIZER = 'json',
    CELERY_TIMEZONE = 'Asia/Chongqing',
)
app.config_from_object('django.conf:settings')
app.autodiscover_tasks()
@periodic_task(run_every=crontab(minute='*/1'), name='send_core_check')
def send_core_check():
    print('send core is running')
@shared_task(default_retry_delay=5, max_retries=3)
def check_one(taskid):
    try:
        new_message = func.check_one(taskid)
        if len(new_message) > 0:
            task = Task.objects.filter(id=taskid)[0]
            title = 'new uestc grade of %s' % task.comment
            plain = json.dumps(new_message, ensure_ascii=False, indent=4)
            async_select_and_send.delay(task.person.email, title, plain)  // send
            func.plus()
    except Exception as e:
        return check_one.retry(taskid)

demo APP

1. __init__.py初始化数据

2. task.py定义任务和规则

3. func.py包含更细节的方法

4. models.py定义了数据表

谢谢

中期答辩

By lc

中期答辩

  • 665