2019年10月

nginx配置

配置nginx上传文件限制

新建一个配置用于存放文件限制
[root@wkjhost ~]# vi /etc/nginx/conf.d/file.conf

server_tokens on;
#优化服务器域名的散列表大小 
server_names_hash_bucket_size 64;
server_names_hash_max_size 2048;
sendfile on;#开启高效文件传输模式
tcp_nopush on;#减少网络报文段数量
tcp_nodelay on;#提高I/O性能
keepalive_timeout 600;#连接超时时间定义
client_header_timeout 600;#读取客户端请求头数据的超时时间
client_body_timeout 600;#读取客户端请求主体的超时时间
send_timeout 600;#响应客户端的超时时间
client_max_body_size 100m;#上传文件的大小限制

驱动点

一、传统H5(高频更新)项目中变量监控、跨浏览器监控是很有必要的
二、保存的数据需要在仓库中处理之后再返回
三、变量实时保存到localStorage或sessionStorage

仓库核心代码如下

//仓库类,设置数据、变量监控、localStorage监控,自定义钩子函数
var myStore = {
    _options: {
        error: null,//错误回调
        watch: {},//监听变量的变化(数组、数字、字符串)
    },
    //定义仓库核心
    _store: function(options){
        var data = {
            _options: {
                error: null,//错误回调
                watch: {},//监听的对象
                methods: {},//方法
                initData: {},//初始化的值
                static: {},//静态数据区
                watchStatic: {},//监听静态变量(不要与静态区键值相同,否则出现数据丢失问题)
            },
            _d: {},//保存运行的数据
            _wd: {},//监控数据的值
            _sd: {},//静态数据区(跨页面访问)

            //设置值
            _set: function(key, value){
                var self = data;
                if(typeof self._sd[key] !== 'undefined'){//静态数据区
                    self._sd[key]  = value;
                    localStorage.setItem(key, JSON.stringify(value));
                }else{
                    self._d[key] = value;
                }
            },

            //设置多个值
            _sets: function(obj){
                var self = data;
                if(typeof obj === "object" && obj !== null){
                    for(var i in obj) {
                        self._set(i, obj[i]);
                   }
                }else{
                    console.error('yexStore的sets使用方法不正确,需要一个对象做为参数');
                }
                return self;
            },

            //获取值
            _get: function(key){
                var self = data;
                if(typeof self._sd[key] !== 'undefined'){//静态数据区
                    var returnData =  localStorage.getItem(key);
                    try {
                        returnData = JSON.parse(returnData);
                    } catch (error) {}
                    return returnData;
                }else if(typeof self._wd[key] !== 'undefined'){
                    return self._wd[key];
                }else{
                   return self._d[key]; 
                }
            },

            //合并对象,并监听对象
            _mergeOptions: function(options){
                var self = this;
                //设置初始化数据
                if(typeof options.initData === 'object' && options.initData !== null ){
                    Object.assign(self._d, options.initData);
                    delete options.initData;
                }
                //静态数据区
                if(typeof options.static === 'object' && options.static !== null){
                    Object.assign(self._sd, options.static);
                    //读取静态数据区在sessionStore的值
                    var item = null;//子项
                    for(var i in self._sd){
                        item = self._sd[i];
                        if(typeof item === 'undefined' || item === null){
                            item = localStorage.getItem(i);
                            try {
                                self._sd[i] = JSON.parse(item);
                            } catch (e) {} 
                        }else{
                            localStorage.setItem(i, item);
                        }
                    }
                    delete options.static;
                }

                //处理方法合并
                if(typeof options.methods === 'object' && options.methods !== null){
                    var item = null;//临时数据
                    for(var i in options.methods){
                        var item = options.methods[i];
                        if(typeof item === 'function'){
                            self[i] = item;
                        }
                    }
                    delete options.methods;
                }

                //合并其他选项
                Object.assign(self._options, options);

                //处理监听区数据
                var watch = self._options.watch;
                if(typeof watch === 'object' && watch !== null){    
                    for(var i in watch){
                        self._watchData(i, watch[i]);
                    }
                }

                //启动静态监控(监听storage事件)
                var watchStatic = self._options.watchStatic;
                for(var i in watchStatic){
                    localStorage.removeItem(i);//移除了key才能再次触发消息
                }
                window.addEventListener('storage', function(e){
                    var key = e.key;
                    var watchStatic = self._options.watchStatic;
                    if(watchStatic && typeof watchStatic[key] === 'function'){
                        var value = e.newValue;
                        if(value !== null){
                            try{value = JSON.parse(value);}catch(e){};
                            watchStatic[key](value);
                        }
                        localStorage.removeItem(key);//移除了key才能再次触发消息
                    }
                });
            },

            _watchData: function(attribute, setFunc){
                if(typeof attribute !== 'string' || attribute.replace(/\s*/g,'') === ''){
                    return;
                }
                var self = this;
                var obj = self._d;
                try{
                    self._wd[attribute] = self._d[attribute];//初始化
                    Object.defineProperty(obj, attribute, {
                        set: function(newValue) {
                            self._wd[attribute] = newValue;
                            setFunc(newValue);//设置值监听
                        }
                    });
                }catch(e){
                    console.log('监听的值重复');
                }
                return self;
            },

            _initData: function(){
                
            },
            
            init: function(options){
                var self = this;
                self._mergeOptions(options);
                self._initData();
                return self;
            },
        };//所有的数据
        
        this.dataObj = data.init(Object.assign({}, options));
        //需要对扩展方法进行外层暴露
        if(typeof options.methods === 'object' && options.methods !== null){
            var item = null;//临时数据
            for(var i in options.methods){
                var item = options.methods[i];
                if(typeof item === 'function'){
                    this[i] = this.dataObj[i];
                }
            }
        }
        this.set = this.dataObj._set;
        this.sets = this.dataObj._sets;
        this.get = this.dataObj._get;
    },

    _storeObj: null,//实例化的仓库对象

    //设置仓库的数据
    set: function(key, value){
        var self = yexStore;
        self._storeObj.set(key, value);
        return self;
    },
    sets: function(obj){
        var self = this;
        self._storeObj.sets(obj);
        return self;
    },

    //获取仓库的数据
    get: function(key){
        var self = this;
        return self._storeObj.get(key);
    },

    //运行方法
    runFunc: function (name, options) {
        var self = yexStore;
        var func = self._storeObj[name];
        if(typeof func !== 'undefined'){
            func(options);
        }else{
            var eFunc = self._options.error;
            if(typeof eFunc  === "function"){
                eFunc('“' + name + '”方法不存在!!!');
            }
        }
    },

    //初始化数据
    _initOptions: function(options){
        var self = this;
         //暴露库里面的方法(作为调用句柄)
         if(typeof options.methods === 'object' && options.methods !== null){
            var item = null;//临时数据
            for(var i in options.methods){
                var item = options.methods[i];
                if(typeof item === 'function'){
                    self[i] = self._storeObj[i];
                }
            }
        }
    },
    
    //初始化仓库
    init: function(options){
        var self = this;
        self._storeObj = new self._store(Object.assign({},options));
        self._initOptions(options);//初始化配置项,需要动态对函数的名字进行定义,一定需要放在最后
        return self;
    }
};

使用方式

//初始化仓库
var store = myStore.init({
    initData: {
        sysRunStartTime: 0,//系统开始运行时间
        classId: 0,//使用的班级id
        lessonId: 1,//使用的课程id
        pageIndex: null//页面记录
    },
    watch: {
        //页面索引(0首页、1点评页、2作业布置页、3授课、4点名页)
        pageIndex: function (index) {
            var self = store;
            console.log('监听到页面变化', pageIndex);
        }
    },
    watchStatic: {
        pageChange: function(value){
            console.log('监听到localStroage变化', value);
        }    
    },
    static: {
        token: null,
        host: config.host,//配置请求的地址
        //静态区监控对象-不是为了存储数据而是为了监控数据(指明存储类型为静态)
        pageChange: null
    },
    methods: {
        //添加页面变化监控
        addPageListen: function (callback) {
            var self = this;
            var name = 'pageListenList';
            if (typeof self[name] !== 'object' || !Array.isArray(self[name])) {
                self[name] = [];
            }
            self[name].push(callback);
            return self;
        }
    }
});

触发点

    //触发localStrage监控(特定版本有效-比如: chrome 69)
    store.set('pageChange', 1);
    //触发变量监听(通用)
    store.set('pageIndex', 1);

概述

适用:两台服务之间实现备份
功能:文件备份、定时执行、数据库备份
服务器: 两台 centos 7 服务器
注1: 服务器(主-base): 192.168.0.3 服务器(从-backup):192.168.0.2
注2: 主服务器自动备份到从服务器

安装基础工具

一、安装rsync及两台机器进行文件同步

1) 安装rsync

[root@wkjhost ~]# yum -y install rsync

2) rsync自启动

[root@wkjhost ~]# systemctl enable rsyncd.service

3) 修改rsync配置文件(修改或者追加)

[root@wkjhost ~]# vi /etc/rsyncd.conf

 uid = root
 gid = root
 use chroot = yes
 max connections = 4
 pid file = /var/run/rsyncd.pid
 exclude = lost+found/
4) 启动rsync

[root@wkjhost ~]# systemctl start rsyncd.service

4) 安装 crontabs服务并设置开机自启

[root@wkjhost ~]# yum install crontabs
[root@wkjhost ~]# systemctl enable crond
[root@wkjhost ~]# systemctl start crond

服务器同步部署

一、实现免密登录(备份服务器可以免密登录基础服务器)

1) 生成ssh密钥(可以直接下一步)

[root@backup ~]# ssh-keygen -t rsa

2) 复制公钥到基础服务器

[root@backup ~]# ssh-copy-id root@192.168.0.3

二、文件同步脚本

1) 书写rysnc-sys脚本

[root@backup ~]# vi /var/crontab-scripts/rsync-sys.sh

#!/bin/bash
# /var/crontab-scripts/rsync-sys.sh
# base system sync to backup system
#from base system gogs-repositories to backup system
rsync -r 192.168.0.1:/home/git/test /home/git
2) 启动定时任务(每5分钟同步一次)

[root@backup ~]# crontab -e

*/5 * * * * /var/crontab-scripts/rsync-sys.sh 

二、mariadb开启主从分布(注:两台机器的mariadb的root用户密码:root) - 主从复制方式

1) 主库(master)开启(设置主库记录操作日志)

设置主mariadb操作日志记录并标记为主mariadb
[root@base~]# vi /etc/my.cnf.d/server.cnf

[mysqld]
server_id=1                 #设置当前服务器的ID号(1主Mariadb,2从Mariadb)
log_bin=mariadb-bin         #启动二进制日志并指定文件名
skip_name_resolve=on        #跳过主机名解析。在CentOS 6自带的mysql后面的=on不用写
innodb_file_per_table=on    #innodb的每个表是用单独的文件

登录数据库(用户名: root 密码: root)
[root@base~]# mysql -uroot -proot
创建从mariadb登录主服务器用户(用户:backupuser 密码:backup_pwd )
MariaDB [(none)]> GRANT REPLICATION SLAVE,REPLICATION CLIENT ON . TO 'backupuser'@'192.168.0.%' IDENTIFIED BY 'backup_pwd';
刷新权限表
MariaDB [(none)]> flush privileges;
查看状态主mariadb状态
MariaDB [(none)]> show master status;
主mariadb状态图.png

从mariadb需要使用(日志文件名(File): mariadb-bin.00001 , 位置(Position): 245)

1) 从库(slave)开启

设置主mariadb操作日志记录并标记为主mariadb
[root@backup ~]# vi /etc/my.cnf.d/server.cnf

[mysqld]
server_id=2          #设置当前服务器的ID号(1主Mariadb,2从Mariadb)
relay_log=relay-log  #启用中继日志,保存当前的中继日志中主节点二进制文件的名字和位置。
read_only=on         #禁止用户写入数据,这一项的管理员和复制重放无效。

重启mariadb服务
[root@backup ~]# systemctl restart mariadb
登录数据库(用户名: root 密码: root)
[root@backup ~]# mysql -uroot -proot
从服务器启动read_only,但仅对非SUPER权限的用户有效
MariaDB [(none)]> flush tables with read lock;
连接主mariadb, 需要使用到主mariadb信息【创建的用户、主mariadb状态信息】
MariaDB [(none)]> CHANGE MASTER TO MASTER_HOST='192.168.0.3',MASTER_USER='backupuser',MASTER_PASSWORD='backup_pwd ',MASTER_LOG_FILE='mariadb-bin.00001',MASTER_LOG_POS=245;
启动从mariadb线程
MariaDB [(none)]> start slave;
查看从mariadb线程是否正确
MariaDB [(none)]> show slave stgatus \G;
从服务器状态.png

Slave_IO_Running是复制线程,Slave_SQL_TRunning是重放线程,两个值都为Yes时候配置成功,否则看截图下面Error的那几个字段,比如连接不成功,密码错误等
测试主从复制是否生效
在主maridb中创建数据库,查看从mariadb是否已经同步

注:如果已经存在的数据库需要要首先导入之后再开启主从复制

二、mariadb双主复制:互为主从:两个节点各自都要开启binlog和relay log

步骤都差不多,只是需要注意几个事项:

1、server_id必须要使用不同值
2、均启用binlog和relay log
3、存在自动增长id的表,为了使得id不相冲突,需要定义其自动增长方式
4、都授权有复制权限的用户账号
5、各把对方指定为主节点

添加节点代码

定义一个节点使用奇数id

[mysqld]
auto_increment_offset=1         #自动增长ID初始数
auto_increment_increment=2      #每次增长几位数

另一个节点使用偶数id

[mysqld]
auto_increment_offset=2
auto_increment_increment=2

概述

适用:新的服务器部署基础服务
功能: 安装mariadb、php、nginx
服务器: centos 7

安装基础工具

一、安装上传与下载工具lrzsz

[root@wkjhost ~]# yum install -y lrzsz

二、安装 yum 工具包:

[root@wkjhost ~]# yum install yum-utils

三、安装EPEL(Extra Packages for Enterprise Linux)软件包

[root@wkjhost ~]# yum install epel-release

四、安装remi(包含最新版本 PHP 和 MySQL 包的 Linux 源)

[root@wkjhost ~]# yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm

五、安装源之后需要更新

[root@wkjhost ~]# yum update

六、安装git工具

[root@wkjhost ~]# yum install git

安装WEB需要的工具

一、mariadb安装:

  1、安装mariadb

[root@wkjhost ~]# yum install mariadb-server

  2、自启动mariadb:

[root@wkjhost ~]# systemctl enable mariadb

  3、配置mariadb默认字符集(修改配置之前需要停止服务,删除/var/lib/mysql下面日志文件)
1) 修改/etc/my.cnf(在[mysqld]标签下添加)

init_connect='SET collation_connection = utf8_general_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_general_ci
skip-character-set-client-handshake
max_allowed_packet=512M
innodb_log_file_size=256M

2) 修改/etc/my.cnf.d/client.cnf(在[client]标签下添加)

default-character-set=utf8

3) 修改/etc/my.cnf.d/mysql-clients.cnf(在[mysql]标签下添加)

default-character-set=utf8

4) 重启数据库,并验证字符集是否设置成功

[root@wkjhost ~]# systemctl start mariadb
[root@wkjhost ~]# mysql -uroot -p
MariaDB [(none)]> show variables like "%character%";show variables like "%collation%";

  4). 设置数据密码(初始密码为空:直接回车就可以)

[root@wkjhost ~]# mysql_secure_installation

二、安装PHP72

  1、启动remi-php72

[root@wkjhost ~]# yum-config-manager --enable remi-php72

  2、安装 PHP7.2

[root@wkjhost ~]# yum install php72

  3、安装 php-fpm 和一些其他模块

[root@wkjhost ~]# yum install php72-php-fpm php72-php-gd php72-php-json php72-php-mbstring php72-php-mysqlnd php72-php-xml php72-php-xmlrpc php72-php-opcache php72-php-zip

  4、设置php-fpm自启动

[root@wkjhost ~]# systemctl enable php72-php-fpm.service

三、安装nginx

  1、创建nginx的yum源

[root@wkjhost ~]# vi /etc/yum.repos.d/nginx.repo

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
  2、启动nginx的yum源

[root@wkjhost ~]# yum-config-manager --enable nginx-mainline

  3、安装nginx(nginx脚本:/usr/sbin/nginx,配置文件: /etc/nginx)

[root@wkjhost ~]# yum install nginx

  4、nginx自启动

[root@wkjhost ~]# systemctl enable nginx

  5、nginx配置为php服务器,代理服务器
1) 配置PHP服务器
server {
    listen 443 ssl;
    server_name www.kjwoo.cn kjwoo.cn;
    root /var/www/typecho;
    index  index.php index.html index.htm;
    access_log /var/www/logs/www.kjwoo.cn-access.log  main;
    error_log /var/www/logs/www.kjwoo.cn-error.log;
    ssl_certificate /etc/nginx/ssl/www.kjwoo.cn/me.crt;
    ssl_certificate_key /etc/nginx/ssl/www.kjwoo.cn/me.key;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico|svg)$ {
        valid_referers none blocked *.kjwoo.cn server_names ~\.google\. ~\.baidu\. ~\.hcs427\.;
        if ($invalid_referer){
            return 301 $http_referer;
        }
        expires 7d;
        access_log off;
    }
    location ~ .*\.(js|css|css\.map) {
        expires 12h;
        access_log off;
    }
    location / {
        if (!-e $request_filename) {
            rewrite  ^/(.*)$  /index.php?s=$1  last;
            break;
        }
        try_files $uri $uri/ index.php /index.php?$QUERY_STRING;
    }
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_read_timeout 600;
        include fastcgi_params;
    }
}
server {
    listen 80;
    server_name www.kjwoo.cn;
    add_header Strict-Transport-Security max-age=15768000;
    return 301 https://$server_name$request_uri;
}
server {
    listen 80;
    server_name kjwoo.cn;
    add_header Strict-Transport-Security max-age=15768000;
    return 301 https://www.$server_name$request_uri;
}
2) 配置代理服务器
server {
    listen 443 ssl;
    server_name  git.kjwoo.cn;
    ssl_certificate /etc/nginx/ssl/git.ky.kjwoo.cn/me.crt;
    ssl_certificate_key /etc/nginx/ssl/git.kjwoo.cn/me.key;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    location / {
        proxy_connect_timeout 1;
        proxy_send_timeout 300;
        proxy_read_timeout 300;
        proxy_buffer_size 1M;
        proxy_buffers 8 1M;
        proxy_busy_buffers_size 1M;
        proxy_temp_file_write_size 1M;
        client_max_body_size 100m;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:3000;
    }
}
server {
    listen 80;
    server_name git.kjwoo.cn;
    rewrite ^(.*)$ https://$host$1 permanent;
}
3) 配置方向代理服务器
server {
    listen 443 ssl;
    server_name  git.kjwoo.cn;
    ssl_certificate /etc/nginx/ssl/git.ky.kjwoo.cn/me.crt;
    ssl_certificate_key /etc/nginx/ssl/git.kjwoo.cn/me.key;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    location / {
        proxy_connect_timeout 1;
        proxy_send_timeout 300;
        proxy_read_timeout 300;
        proxy_buffer_size 1M;
        proxy_buffers 8 1M;
        proxy_busy_buffers_size 1M;
        proxy_temp_file_write_size 1M;
        client_max_body_size 100m;
        proxy_set_header Host  $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:3000;
    }
}
server {
    listen 80;
    server_name git.kjwoo.cn;
    rewrite ^(.*)$ https://$host$1 permanent;
}

三、问题解决

  1、applydeltarpm问题

[root@wkjhost ~]# yum provides */applydeltarpm
[root@wkjhost ~]# yum install deltarpm -y

站点安全需求:

  1. 防盗链(保护站点资源)
  2. 防止一些乱七八糟的爬虫与非法访问
  3. 保证表单安全(SSL)
  4. 启动一个比较弱的cookie
  5. 启动Robots协议
  6. 去除弱爬虫(使用短连接访问动态文件)

实现:
一、防盗链(除了本站点、谷歌、百度才能获取资源,其他的全部转向到自己站点)
在nginx主机配置中的server中添加代码:

valid_referers none blocked *.kjwoo.cn server_names ~\.google\. ~\.baidu\.;
if ($invalid_referer){
    return 301 $http_referer;
}

二、防止乱七八糟的访问(返回403错误)
在nginx主机配置中的server中添加代码:

if ($http_user_agent ~ "FeedDemon|JikeSpider|Indy Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|CoolpadWebkit|Java|Feedly|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|YisouSpider|HttpClient|MJ12bot|heritrix|EasouSpider|LinkpadBot|Ezooms|^$"){
        return 403;
 }

3、保证表单安全(SSL)
在nginx主机配置中的server中添加代码:

ssl_certificate /etc/nginx/ssl/www.kjwoo.cn/me.crt;
ssl_certificate_key /etc/nginx/ssl/www.kjwoo.cn/me.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

四、启动cookie (第一次访问限制速度为:128k)
在nginx主机配置中的server中添加代码:

if ($cookie_kanjin != "kanjin$remote_addr"){
    limit_rate 128;
    add_header Set-Cookie "kanjin=kanjin$remote_addr";
    return 302 $scheme://$host$request_uri;
}

五、启动Robots协议(允许所有爬虫,但是不能爬/admin目录下面的资源)
在网站跟目录添加:robots.txt:

User-agent:*
Disallow:/admin

六、去除弱爬虫(使用短连接访问动态文件)
在网站的入口文件中添加判断(如果符合就直接重定向到百度)-以PHP为例子

if($_SERVER['HTTP_CONNECTION'] != 'keep-alive'){
    header("Location:https://www.baidu.com");exit;//重定向到百度
}

概述

从PPT在线编辑1.0版本逐步过渡到2.0版本,其中经历版本如下:

一、1.1动画版本
二、1.2形状和组合版本
三、1.3 GGB与支持其他资源插入PPT版本
四、2.0自研渲染引擎版本。 

目标

一、1.1动画版本

1、实现动画可以编辑
2、支持插入动画
注:支持大部分基本动画

二、1.2形状和组合版本

1、实现形状可以编辑
2、组合操作插入
3、背景修改与设置

三、1.3 GGB与支持其他资源插入PPT版本

1、GGB插入PPT
2、题目优化
3、其他资源支持

四、2.0 自研PPT渲染引擎版本

1、使用SPR数据使用自研PPT渲染引擎展示
2、支持在线编辑PPT
3、支持创建PPT
4、完全翻译SPR展示

大体工作计划(时间颗粒为周)

一、1.1动画版本(5周开发)

1)SPR动画结构研究与标准动画数据结构规划
2)SVG编辑器开发与标准动画数据结构确定
3)标准动画数据结构与SPR渲染引擎交互(修改SPR中动画)
4)添加动画交互制作(基本一个动画一天)
5)动画属性栏优化、API联调、课堂端联调、代码优化、压缩

二、1.2形状和组合版本(2周开发)

1)SVG形状开发,插入与修改
2)组合(对象)模式设计,快捷键支持,组合属性栏调整

三、1.3 GGB与支持其他资源插入PPT版本(2周开发)

1)GGB插入PPT模式、GGB属性栏与交互
2)题库优化、GGB动画支持与其他资源插入支持,快捷键支持、性能优化

四、2.0 自研PPT渲染引擎版本(4周开发)

1)架构选型、调试模式建立、工具类封装
2)自研PPT渲染引擎开发
3)SPR操作迁移到自研
4)快捷键支持、交互优化、代码优化、打包,渲染端(课堂端)兼容多种模式

估计前端开发总时长:5 + 2 + 2 + 4 = 13周