11.王道_workflow

  • 如何部署http服务器
    • 从零开始
    • 使用开源的产品
      • nginx,apache
    • 库 or 框架
      • workflow
      • boost::asio(TCP),workflow
      • boost::beast(HTTP)(基于asio)

服务器类型

  1. 原始的多进程服务器(以Apache为例)

  2. 事件驱动服务器(Event-Driven)
    在这里插入图片描述
    在这里插入图片描述

  3. 异步回调模型(Asynchronous Callback)
    在这里插入图片描述
    在这里插入图片描述

  • sql语句的执行
    • 事件驱动模型:准备工作和返回结果分为两个事件。
    • 异步回调模型:准备的sql语句作为任务的准备工作,读到以后的事情作为回调函数。
    • 协程:从头写到尾部。

workflow简介

在这里插入图片描述

安装workflow

在这里插入图片描述
在这里插入图片描述

workflow是一种异步框架

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


#include <54func.h>
#include <workflow/WFFacilities.h>

static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    gWaitGroup.wait(); // 等待直到计数器减一f

    return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

使用workflow作为HTTP客户端

在这里插入图片描述

避免进程提前终止

在这里插入图片描述

http客户端的基本流程

  • 只有请求
#include <54func.h>
#include<iostream>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,nullptr);
    // 设置httpTask的属性和请求
    cout<<"2"<<endl;
    protocol::HttpRequest *req = httpTask->get_req();
    req->add_header_pair("Accept","*/*");
    // 将任务派给框架,由g框架异步调用
    httpTask->start();
    cout<<"3"<<endl;
    gWaitGroup.wait(); // 等待直到计数器减一f
    return 0;
}

在这里插入图片描述

  • 输出3:立刻开始执行,异步的特点

  • 有请求有响应(重写回调函数)

#include <54func.h>
#include<iostream>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 有请求有响应

void httpCallback(WFHttpTask *httpTask)
{
    cout<<"4"<<endl;
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,httpCallback);
    // 设置httpTask的属性和请求
    cout<<"2"<<endl;
    protocol::HttpRequest *req = httpTask->get_req();
    req->add_header_pair("Accept","*/*");
    // 将任务派给框架,由g框架异步调用
    httpTask->start();
    cout<<"3"<<endl;
    gWaitGroup.wait(); // 等待直到计数器减一f
    return 0;
}

注意这里的3和4的前后顺序不确定
在这里插入图片描述

#include <54func.h>
#include<iostream>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 有请求有响应

void httpCallback(WFHttpTask *httpTask)
{
    cout<<"4"<<endl;
    // 这里就可以处理响应的请求了
    protocol::HttpRequest *req = httpTask->get_req();
    protocol::HttpResponse *resp = httpTask->get_resp();

    int state = httpTask->get_state();
    int error = httpTask->get_error();

    switch(state)
    {
    case WFT_STATE_SYS_ERROR:   // 系统级的错误
        cout<<"系统级错误 :"<<strerror(error)<<endl;
        break;
    case WFT_STATE_DNS_ERROR:   // 网络连接出现问题
        cout<<"DNS Error: "<<gai_strerror(error)<<endl;
        break;
    case WFT_STATE_SUCCESS:     // 成功
        break;
    }

    if(state == WFT_STATE_SUCCESS)
    {
        cout<<"SUCCESS"<<endl;
    }
    else
    {
        cout<<"FAILS"<<endl;
    }
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,httpCallback);
    // 设置httpTask的属性和请求
    cout<<"2"<<endl;
    protocol::HttpRequest *req = httpTask->get_req();
    req->add_header_pair("Accept","*/*");
    // 将任务派给框架,由g框架异步调用
    httpTask->start();
    cout<<"3"<<endl;
    gWaitGroup.wait(); // 等待直到计数器减一f
    return 0;
}
  • 不启动服务端时
    在这里插入图片描述

  • 启动服务端之后还是上面的报错。发现是没加入冒号
    在这里插入图片描述

  • 修改之后正确
    在这里插入图片描述

遍历首部字段

  • 由于首部字段比较小,所以workflow将请求和响应的首部字段放在一起

在这里插入图片描述

#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
using std::string;

static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 有请求有响应

void httpCallback(WFHttpTask *httpTask)
{
    cout<<"4"<<endl;
    // 这里就可以处理响应的请求了
    protocol::HttpRequest *req = httpTask->get_req();
    protocol::HttpResponse *resp = httpTask->get_resp();

    int state = httpTask->get_state();
    int error = httpTask->get_error();

    switch(state)
    {
    case WFT_STATE_SYS_ERROR:   // 系统级的错误
        cout<<"系统级错误 :"<<strerror(error)<<endl;
        break;
    case WFT_STATE_DNS_ERROR:   // 网络连接出现问题
        cout<<"DNS Error: "<<gai_strerror(error)<<endl;
        break;
    case WFT_STATE_SUCCESS:     // 成功
        break;
    }

    if(state == WFT_STATE_SUCCESS)
    {
        cout<<"SUCCESS"<<endl;
    }
    else
    {
        cout<<"FAILS"<<endl;
    }
    protocol::HttpHeaderCursor  cursorReq(req);
    string key,value;
    while(cursorReq.next(key,value))
    {
        cout<<"request key = "<<key<<" value: "<<value<<endl;
    }
    protocol::HttpHeaderCursor  cursorResp(resp);
    cout<<"______________________________"<<endl;
    while(cursorResp.next(key,value))
    {
        cout<<"response key = "<<key<<" value: "<<value<<endl;
    }
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,httpCallback);
    // 设置httpTask的属性和请求
    cout<<"2"<<endl;
    protocol::HttpRequest *req = httpTask->get_req();
    req->add_header_pair("Accept","*/*");
    // 将任务派给框架,由g框架异步调用
    httpTask->start();
    cout<<"3"<<endl;
    gWaitGroup.wait(); // 等待直到计数器减一f
    return 0;
}

在这里插入图片描述

处理响应体

  • 由于响应体一般都很大,workflow单独保存一个地方
    在这里插入图片描述

  • 虽然body加const但是实际上一级指针是非const的

void httpCallback(WFHttpTask *httpTask)
{
    cout<<"4"<<endl;
    // 这里就可以处理响应的请求了
    protocol::HttpRequest *req = httpTask->get_req();
    protocol::HttpResponse *resp = httpTask->get_resp();

    int state = httpTask->get_state();
    int error = httpTask->get_error();

    switch(state)
    {
    case WFT_STATE_SYS_ERROR:   // 系统级的错误
        cout<<"系统级错误 :"<<strerror(error)<<endl;
        break;
    case WFT_STATE_DNS_ERROR:   // 网络连接出现问题
        cout<<"DNS Error: "<<gai_strerror(error)<<endl;
        break;
    case WFT_STATE_SUCCESS:     // 成功
        break;
    }

    if(state == WFT_STATE_SUCCESS)
    {
        cout<<"SUCCESS"<<endl;
    }
    else
    {
        cout<<"FAILS"<<endl;
    }
    protocol::HttpHeaderCursor  cursorReq(req);
    string key,value;
    while(cursorReq.next(key,value))
    {
        cout<<"request key = "<<key<<" value: "<<value<<endl;
    }
    protocol::HttpHeaderCursor  cursorResp(resp);
    cout<<"______________________________"<<endl;
    while(cursorResp.next(key,value))
    {
        cout<<"response key = "<<key<<" value: "<<value<<endl;
    }

    const void *body;
    size_t size;
    resp->get_parsed_body(&body,&size);
    cout<<string((char*)body,size)<<endl;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

class HttpMessage : public ProtocolMessage
{
public:
    const char *get_http_version() const;
    bool set_http_version(const char *version);
    bool add_header_pair(const char *name, const char *value);
    bool set_header_pair(const char *name, const char *value);
    bool get_parsed_body(const void **body, size_t *size) const;
    /* Output body is for sending. Want to transfer a message received, maybe:
     * msg->get_parsed_body(&body, &size);
     * msg->append_output_body_nocopy(body, size); */
    bool append_output_body(const void *buf, size_t size);
    bool append_output_body_nocopy(const void *buf, size_t size);
    void clear_output_body();
    size_t get_output_body_size() const;
    // 上述接口都有std::string版本
    //...
};
class HttpRequest : public HttpMessage
{
public:
    const char *get_method() const;
    const char *get_request_uri() const;
    bool set_method(const char *method);
    bool set_request_uri(const char *uri);
    // 上述接口都有std::string版本
    //...
};
class HttpResponse : public HttpMessage
{
public:
    const char *get_status_code() const;
    const char *get_reason_phrase() const;
    bool set_status_code(const char *code);
    bool set_reason_phrase(const char *phrase);
    /* Tell the parser, it is a HEAD response. */
    void parse_zero_body();
    // 上述接口都有std::string版本
    //...
};

在这里插入图片描述

回调函数的设计

在这里插入图片描述


#include "unixHeader.h"
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo)
{
    wait_group.done();
}
void callback(WFHttpTask *httpTask)
{
    int state = httpTask->get_state();
    int error = httpTask->get_error();
    switch (state)
    {
    case WFT_STATE_SYS_ERROR:
        fprintf(stderr, "system error: %s\n", strerror(error));
        break;
    case WFT_STATE_DNS_ERROR:
        fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
        break;
    case WFT_STATE_SUCCESS:
        break;
    }
    if (state != WFT_STATE_SUCCESS)
    {
        fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
        return;
    }
    fprintf(stderr, "success\n");
    wait_group.done();
}
int main(int argc, char *argv[])
{
    std::string url = "http://";
    url.append(argv[1]);
    signal(SIGINT, sig_handler);
    auto httpTask = WFTaskFactory::create_http_task(url, 0, 0, callback);
    protocol::HttpRequest *req = httpTask->get_req();
    req->add_header_pair("Accept", "*/*");
    req->add_header_pair("User-Agent", "TestAgent");
    req->add_header_pair("Connection", "close");
    httpTask->start();
    wait_group.wait();
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

redis任务与串行任务

redis任务的基本使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
using std::string;

static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}


// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,nullptr);
    // 设置redisTask的属性和请求
    protocol::RedisRequest *req = redisTask->get_req();
    req->set_request("SET",{"x","54"});
    // 将任务派给框架,由g框架异步调用
    redisTask->start();
    gWaitGroup.wait(); // 等待直到计数器减一f
    return 0;
}

在这里插入图片描述

处理redis任务的结果

在这里插入图片描述


namespace protocol
{
    class RedisValue
    {
    public:
        // nil
        RedisValue();
        virtual ~RedisValue();
        // copy constructor
        RedisValue(const RedisValue &copy);
        // copy operator
        RedisValue &operator=(const RedisValue &copy);
        // move constructor
        RedisValue(RedisValue &&move);
        // move operator
        RedisValue &operator=(RedisValue &&move);
        // release memory and change type to nil
        void set_nil();
        void set_int(int64_t intv);
        void set_string(const std::string &strv);
        void set_status(const std::string &strv);
        void set_error(const std::string &strv);
        void set_string(const char *str, size_t len);
        void set_status(const char *str, size_t len);
        void set_error(const char *str, size_t len);
        void set_string(const char *str);
        void set_status(const char *str);
        void set_error(const char *str);
        // array(resize)
        void set_array(size_t new_size);
        // set data by C style data struct
        void set(const redis_reply_t *reply);
        // Return true if not error
        bool is_ok() const;
        // Return true if error
        bool is_error() const;
        // Return true if nil
        bool is_nil() const;
        // Return true if integer
        bool is_int() const;
        // Return true if array
        bool is_array() const;
        // Return true if string/status
        bool is_string() const;
        // Return type of C style data struct
        int get_type() const;
        // Copy. If type isnot string/status/error, returns an empty std::string
        std::string string_value() const;
        // No copy. If type isnot string/status/error, returns NULL.
        const std::string *string_view() const;
        // If type isnot integer, returns 0
        int64_t int_value() const;
        // If type isnot array, returns 0
        size_t arr_size() const;
        // If type isnot array, do nothing
        void arr_clear();
        // If type isnot array, do nothing
        void arr_resize(size_t new_size);
        // Always return std::vector.at(pos); notice overflow exception
        RedisValue &arr_at(size_t pos) const;
        // Always return std::vector[pos]; notice overflow exception
        RedisValue &operator[](size_t pos) const;
        // transform data into C style data struct
        bool transform(redis_reply_t *reply) const;
        // equal to set_nil();
        void clear();
        // format data to text
        std::string debug_string() const;
        //...
    };
}
  • 下面是使用redis读类型任务获取一系列结果的例子。
#include "unixHeader.h"
#include <workflow/RedisMessage.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
void redis_callback(WFRedisTask *task)
{
    protocol::RedisRequest *req = task->get_req();
    protocol::RedisResponse *resp = task->get_resp();
    int state = task->get_state();
    int error = task->get_error();
    protocol::RedisValue val;
    switch (state)
    {
    case WFT_STATE_SYS_ERROR:
        fprintf(stderr, "system error: %s\n", strerror(error));
        break;
    case WFT_STATE_DNS_ERROR:
        fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
        break;
    case WFT_STATE_SUCCESS:
        resp->get_result(val);
        if (val.is_error())
        {
            fprintf(stderr, "Error reply. Need a password?\n");
            state = WFT_STATE_TASK_ERROR;
        }
        break;
    }
    if (state != WFT_STATE_SUCCESS)
    {
        fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
        return;
    }
    if (val.is_array())
    {
        for (int i = 0; i < val.arr_size(); ++i)
        {
            auto ele = val.arr_at(i);
            fprintf(stderr, "REDIS HGETALL %d = %s\n", i,
                    ele.string_value().c_str());
        }
    }
}
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo)
{
    wait_group.done();
}
int main(int argc, char *argv[])
{
    signal(SIGINT, sig_handler);
    url = "redis://127.0.0.1:6379";
    WFRedisTask *task;
    = WFTaskFactory::create_redis_task(data.url, 2,
                                       redis_callback);
    protocol::RedisRequest *req = task->get_req();
    // req->set_request("HSET", { "HMAP1","key1", "value1" });
    req->set_request("HGETALL", {"HMAP1"});
    task->start();
    wait_group.wait();
    return 0;
}
  • 读取结果

#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
using std::string;

static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
                                                              [](WFRedisTask *redisTask){
                                                              protocol::RedisRequest *req = redisTask->get_req();
                                                              protocol::RedisResponse *resp = redisTask->get_resp();
                                                                                                                          protocol::RedisValue value ; // 获取redis返回的结果
                                                              int state  = redisTask->get_state();
                                                              int error = redisTask->get_error();
                                                              // error只能处理网络连接和系统错误,语法错误只能通过返回结果查看oo
                                                                                                                          switch (state)
                                                                                                                          {
                                                                                                                          case WFT_STATE_SYS_ERROR:
                                                                                                                                        fprintf(stderr, "system error: %s\n", strerror(error));
                                                                                                                                        break;
                                                                                                                          case WFT_STATE_DNS_ERROR:
                                                                                                                                        fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
                                                                                                                                        break;
                                                                                                                          case WFT_STATE_SUCCESS:
                                                                                                                                        resp->get_result(value);  // 语法错误只能通过返回结果进行查看
                                                                                                                                        if(value.is_error())
                                                                                                                                        {
                                                                                                                                                cout<<"语法错误"<<endl;
                                                                                                                                                state = WFT_STATE_TASK_ERROR; // 任务层面的错误
                                                                                                                                        }
                                                                                                                                        break;
                                                                                                                          }
                                                                                                                          if (state != WFT_STATE_SUCCESS)
                                                                                                                          {
                                                                                                                                        fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
                                                                                                                                        return;
                                                                                                                          }
                                                              if(value.is_string())
                                                              {
                                                                  cout<<"value is a string , value = "<<value.string_value()<<endl;
                                                              }

                                                              });
        // 设置redisTask的属性和请求
        protocol::RedisRequest *req = redisTask->get_req();
        // 是一个写命令
    //req->set_request("SET",{"x","54"});

    // 是一个读命令
    req->set_request("GET",{"x"});
    // 将任务派给框架,由g框架异步调用
        redisTask->start();
        gWaitGroup.wait(); // 等待直到计数器减一f
        return 0;
}

  • 读取复杂数据
    在这里插入图片描述
if (value.is_string())
{
    cout << "value is a string , value = " << value.string_value() << endl;
}
else if (value.is_array())
{
    for (size_t i = 0; i < value.arr_size(); ++i)
    {
        cout << "value is arr,i = " << i << ",arr[i] = " << value.arr_at(i).string_value() << endl;
    }
}

在这里插入图片描述

注意:框架的执行单位是序列

串行任务(序列 serial)

在这里插入图片描述


#include "unixHeader.h"
#include <workflow/RedisMessage.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#define RETRY_MAX 2
struct tutorial_task_data
{
    std::string url;
    std::string key;
};
void redis_callback(WFRedisTask *task)
{
    protocol::RedisRequest *req = task->get_req();
    protocol::RedisResponse *resp = task->get_resp();
    int state = task->get_state();
    int error = task->get_error();
    protocol::RedisValue val;
    switch (state)
    {
    case WFT_STATE_SYS_ERROR:
        fprintf(stderr, "system error: %s\n", strerror(error));
        break;
    case WFT_STATE_DNS_ERROR:
        fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
        break;
    case WFT_STATE_SUCCESS:
        resp->get_result(val);
        if (val.is_error())
        {
            fprintf(stderr, "Error reply. Need a password?\n");
            state = WFT_STATE_TASK_ERROR;
        }
        break;
    }
    if (state != WFT_STATE_SUCCESS)
    {
        fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
        return;
    }
    std::string cmd;
    整个程序的流程执行如下:
    req->get_command(cmd);
    if (cmd == "SET")
    {
        tutorial_task_data *data = (tutorial_task_data *)task->user_data;
        WFRedisTask *next = WFTaskFactory::create_redis_task(data->url,
                                                             RETRY_MAX,
                                                             redis_callback);
        next->get_req()->set_request("GET", {data->key});
        /* Push next task(GET task) to current series. */
        series_of(task)->push_back(next);
        fprintf(stderr, "Redis SET request success. Trying to GET...\n");
    }
    else /* if (cmd == "GET") */
    {
        if (val.is_string())
        {
            fprintf(stderr, "Redis GET success. value = %s\n",
                    val.string_value().c_str());
        }
        else
        {
            fprintf(stderr, "Error: Not a string value. \n");
        }
        fprintf(stderr, "Finished. Press Ctrl-C to exit.\n");
    }
}
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo)
{
    wait_group.done();
}
int main(int argc, char *argv[])
{
    signal(SIGINT, sig_handler);
    struct tutorial_task_data data;
    data.url = "redis://127.0.0.1:6379";
    data.key = "key1";
    WFRedisTask *task = WFTaskFactory::create_redis_task(data.url, RETRY_MAX,
                                                         redis_callback);
    protocol::RedisRequest *req = task->get_req();
    req->set_request("SET", {data.key, "value2"});
    /* task->user_data is a public (void *), can store anything. */
    task->user_data = &data;
    task->start();
    wait_group.wait();
    return 0;
}

在这里插入图片描述

f

#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::endl;
using std::string;

static WFFacilities::WaitGroup gWaitGroup(1);  // 计数器

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT,handler);
    cout<<"1"<<endl;
    // 利用工厂函数创建一个任务
    WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
                                                              [](WFRedisTask *redisTask){
                                                             sleep(1);
                                                             cout<<"Task 1 sleep over"<<endl;
                                                              protocol::RedisRequest *req = redisTask->get_req();
                                                              protocol::RedisResponse *resp = redisTask->get_resp();
                                                                                                                          protocol::RedisValue value ; // 获取redis返回的结果
                                                              int state  = redisTask->get_state();
                                                              int error = redisTask->get_error();
                                                              // error只能处理网络连接和系统错误,语法错误只能通过返回结果查看oo
                                                                                                                          switch (state)
                                                                                                                          {
                                                                                                                          case WFT_STATE_SYS_ERROR:
                                                                                                                                        fprintf(stderr, "system error: %s\n", strerror(error));
                                                                                                                                        break;
                                                                                                                          case WFT_STATE_DNS_ERROR:
                                                                                                                                        fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
                                                                                                                                        break;
                                                                                                                          case WFT_STATE_SUCCESS:
                                                                                                                                        resp->get_result(value);  // 语法错误只能通过返回结果进行查看
                                                                                                                                        if(value.is_error())
                                                                                                                                        {
                                                                                                                                                cout<<"语法错误"<<endl;
                                                                                                                                                state = WFT_STATE_TASK_ERROR; // 任务层面的错误
                                                                                                                                        }
                                                                                                                                        break;
                                                                                                                          }
                                                                                                                          if (state != WFT_STATE_SUCCESS)
                                                                                                                          {
                                                                                                                                        fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
                                                                                                                                        return;
                                                                                                                          }
                                                              if(value.is_string())
                                                              {
                                                                  cout<<"value is a string , value = "<<value.string_value()<<endl;
                                                              }
                                                              else if(value.is_array())
                                                              {
                                                                  for(size_t i = 0;i<value.arr_size();++i)
                                                                  {
                                                                      cout<<"value is arr,i = "<<i<<",arr[i] = "<<value.arr_at(i).string_value()<<endl;
                                                                  }
                                                              }

                                                              });
        // 设置redisTask的属性和请求
        protocol::RedisRequest *req1 = redisTask->get_req();
        // 是一个写命令
    req1->set_request("SET",{"54","ABC"});





    WFRedisTask *redisTask2 = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
                                                              [](WFRedisTask *redisTask){
                                                              protocol::RedisRequest *req = redisTask->get_req();
                                                              protocol::RedisResponse *resp = redisTask->get_resp();
                                                                                                                          protocol::RedisValue value ; // 获取redis返回的结果
                                                              int state  = redisTask->get_state();
                                                              int error = redisTask->get_error();
                                                              // error只能处理网络连接和系统错误,语法错误只能通过返回结果查看oo
                                                                                                                          switch (state)
                                                                                                                          {
                                                                                                                          case WFT_STATE_SYS_ERROR:
                                                                                                                                        fprintf(stderr, "system error: %s\n", strerror(error));
                                                                                                                                        break;
                                                                                                                          case WFT_STATE_DNS_ERROR:
                                                                                                                                        fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
                                                                                                                                        break;
                                                                                                                          case WFT_STATE_SUCCESS:
                                                                                                                                        resp->get_result(value);  // 语法错误只能通过返回结果进行查看
                                                                                                                                        if(value.is_error())
                                                                                                                                        {
                                                                                                                                                cout<<"语法错误"<<endl;
                                                                                                                                                state = WFT_STATE_TASK_ERROR; // 任务层面的错误
                                                                                                                                        }
                                                                                                                                        break;
                                                                                                                          }
                                                                                                                          if (state != WFT_STATE_SUCCESS)
                                                                                                                          {
                                                                                                                                        fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
                                                                                                                                        return;
                                                                                                                          }
                                                              if(value.is_string())
                                                              {
                                                                  cout<<"value is a string , value = "<<value.string_value()<<endl;
                                                              }
                                                              else if(value.is_array())
                                                              {
                                                                  for(size_t i = 0;i<value.arr_size();++i)
                                                                  {
                                                                      cout<<"value is arr,i = "<<i<<",arr[i] = "<<value.arr_at(i).string_value()<<endl;
                                                                  }
                                                              }

                                                              });
        // 设置redisTask的属性和请求
        protocol::RedisRequest *req2 = redisTask2->get_req();
        // 是一个写命令
    req2->set_request("GET",{"54"});

    //  创建一个x序列
    SeriesWork *series = Workflow::create_series_work(redisTask,nullptr);
    series->push_back(redisTask2);
    series->start();
    // 将任务派给框架,由g框架异步调用
        gWaitGroup.wait(); // 等待直到计数器减一f
        return 0;
}


在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

回调任务中创建任务

在这里插入图片描述
在这里插入图片描述

在序列执行过程中增加任务

在这里插入图片描述

在这里插入图片描述

在序列的任务之间共享数据

在这里插入图片描述


#include <54func.h>
#include <iostream>
#include <string>
#include <workflow/WFFacilities.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>

using std::cout;
using std::endl;
using std::string;

struct SeriesContext
{
    int i;
    string str;
};
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器

void handler(int signum)
{
    printf("signum = %d\n", signum);
    gWaitGroup.done(); // 计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
    signal(SIGINT, handler);
    cout << "1" << endl;
    // 利用工厂函数创建一个任务
    WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379", 0,
                                                              [](WFRedisTask *redisTask)
                                                              {
                                                                  sleep(1);
                                                                  cout << "Task 1 sleep over" << endl;
                                                                  protocol::RedisRequest *req = redisTask->get_req();
                                                                  protocol::RedisResponse *resp = redisTask->get_resp();
                                                                  protocol::RedisValue value; // 获取redis返回的结果
                                                                  resp->get_result(value);

                                                                  SeriesContext *pContext = new SeriesContext;
                                                                  series_of(redisTask)->set_context(pContext);
                                                                  pContext->i = 1;
                                                                  pContext->str = "hello";

                                                                  if (value.is_string() && value.string_value() == "OK")
                                                                  {
                                                                      cout << "Task 1 value is a string , value = " << value.string_value() << endl;
                                                                      cout << "Task 2 is create " << endl;
                                                                      WFRedisTask *redisTask2 = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379", 0,
                                                                                                                                 [](WFRedisTask *redisTask)
                                                                                                                                 {
                                                                                                                                     protocol::RedisRequest *req = redisTask->get_req();
                                                                                                                                     protocol::RedisResponse *resp = redisTask->get_resp();
                                                                                                                                     protocol::RedisValue value; // 获取redis返回的结果
                                                                                                                                     resp->get_result(value);
                                                                                                                                     SeriesContext *pContext = (SeriesContext *)(series_of(redisTask)->get_context());
                                                                                                                                     // 但是何时释放pContext,可以放在序列中的回调函数中进行释放

                                                                                                                                     if (value.is_string())
                                                                                                                                     {
                                                                                                                                         cout << "Task 2 " << value.string_value() << endl;
                                                                                                                                         cout << "Task 2 i = " << pContext->i << "  str=" << pContext->str << endl;
                                                                                                                                     }
                                                                                                                                 });
                                                                      protocol::RedisRequest *req2 = redisTask2->get_req();
                                                                      req2->set_request("GET", {"54"});
                                                                      // redisTask2->start();
                                                                      series_of(redisTask)->push_back(redisTask2); // 找到当前所在的序列,并将Task2加入其中
                                                                  }
                                                                  else
                                                                  {
                                                                      cout << "Task 2 is not create" << endl;
                                                                  }
                                                                  // 给序列添加回调函数,释放共享的数据
                                                                  series_of(redisTask)->set_callback(
                                                                      [](const SeriesWork *series)
                                                                      {
                                                                          cout << "Series CallBack" << endl;
                                                                          SeriesContext *pContext = (SeriesContext *)series->get_context();
                                                                          delete pContext;
                                                                      });
                                                              });
    // 在redisTask1回调的过程中创建u一个x新任务
    // 设置redisTask的属性和请求
    protocol::RedisRequest *req1 = redisTask->get_req();
    // 是一个写命令
    req1->set_request("SET", {"54", "A"});
    redisTask->start();

    // 将任务派给框架,由g框架异步调用
    gWaitGroup.wait(); // 等待直到计数器减一f
    return 0;
}

在这里插入图片描述

在这里插入图片描述
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/7c5561df77894646abc5a7b091fb59b3.png

作业

在这里插入图片描述
在这里插入图片描述


#include<54func.h>
#include<iostream>
#include<string>
#include<workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::string;
using std::endl;


static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done();
}

class HomeWork
{
public:
    void init_query()
    {

        WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
                                                                  std::bind(&HomeWork::on_redis_return,this,std::placeholders::_1));
        redisTask->get_req()->set_request("GET",{"x1"});
        redisTask->start();
    }

    void on_redis_return(WFRedisTask *redisTask)
    {

        protocol::RedisResponse *resp = redisTask->get_resp();
        protocol::RedisValue value ; // 获取redis返回的结果
        resp->get_result(value);
        if(value.is_ok() && value.string_value() == "100")
        {
            cout<<"finished"<<endl;
            return;
        }
        else
        {
            cout<<"current value is "<<value.string_value()<<endl;
            sleep(1);
            WFRedisTask *nextRedisTask =
            WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
                        std::bind(&HomeWork::on_redis_return,this,std::placeholders::_1));
            nextRedisTask->get_req()->set_request("GET",{value.string_value()});
            series_of(redisTask)->push_back(nextRedisTask);

        }
    }
};

int main()
{
    signal(SIGINT,handler);
    HomeWork homework;
    homework.init_query();
    gWaitGroup.wait();
    return 0;
}

在这里插入图片描述

在这里插入图片描述




#include<54func.h>
#include<iostream>
#include<string>
#include<workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>

using std::cout;
using std::string;
using std::endl;


static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
    printf("signum = %d\n",signum);
    gWaitGroup.done();
}

class HomeWork
{
public:
    void init_query()
    {
        WFHttpTask *httpTask =
            WFTaskFactory::create_http_task("http://www.taobao.com",10,0,
                            std::bind(&HomeWork::on_http_response,this,std::placeholders::_1));
        httpTask->start();
    }

    void on_http_response(WFHttpTask *httpTask)
    {
        protocol::HttpResponse *resp = httpTask->get_resp();
        const void * body;
        size_t size;
        resp->get_parsed_body(&body,&size);
        string value((char*)body,size);
        WFRedisTask *redisTask =
            WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
            std::bind(&HomeWork::on_redis_return,this,std::placeholders::_1));
        redisTask->get_req()->set_request("SET",{"www.taobao.com",value});
        series_of(httpTask)->push_back(redisTask);
    }

    void on_redis_return(WFRedisTask *redisTask)
    {
        protocol::RedisResponse *resp = redisTask->get_resp();
        protocol::RedisValue value;
        resp->get_result(value);
        if(value.is_ok())
        {
            cout<<"value is "<<value.string_value()<<endl;
        }
    }
};

int main()
{
    signal(SIGINT,handler);
    HomeWork homework;
    homework.init_query();
    gWaitGroup.wait();
    return 0;
}

并行任务(paralell_work)

在这里插入图片描述

使用并行任务的基本流程

在这里插入图片描述
在这里插入图片描述

一个复杂的使用并行任务的例子(并联之后串行)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
using namespace std;
static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void httpCallback(WFHttpTask *httpTask)
{
    cout<<"http Callback"<<endl;
    protocol::HttpResponse *resp = httpTask->get_resp();
    cout<<resp->get_http_version()<<" "
        <<resp->get_status_code()<<" "
        <<resp->get_reason_phrase()<<endl;
    const void * body;
    size_t size;
    resp->get_parsed_body(&body,&size);
    cout<<"szie = "<<size<<endl;
}


int main()
{
    vector<string> vec{
        "http://www.qq.com",
        "http://www.baidu.com",
        "http://www.taobao.com"
    };

    // 创建一个并行任务
    ParallelWork *parallel = Workflow::create_parallel_work(nullptr);
    for(int i =0;i<3;i++)
    {
        // 创建一个简单的http任务
        WFHttpTask *httpTask = WFTaskFactory::create_http_task(vec[i],10,0,httpCallback);
        // 创建序列
        SeriesWork *series = Workflow::create_series_work(httpTask,nullptr);
        // v把序列加入到并行任务中
        parallel->add_series(series);
    }
    parallel->start(); // 启动parrel
    gWaitGroup.wait();
}

在这里插入图片描述

#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
using namespace std;

struct SeriesContext{
    string url;
    size_t size;
};

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void httpCallback(WFHttpTask *httpTask)
{
    cout<<"http Callback"<<endl;
    protocol::HttpResponse *resp = httpTask->get_resp();
    cout<<resp->get_http_version()<<" "
        <<resp->get_status_code()<<" "
        <<resp->get_reason_phrase()<<endl;
    const void * body;
    size_t size;
    resp->get_parsed_body(&body,&size);
    cout<<"size = "<<size<<endl;

    // 回调执行过程中也会写入context
    SeriesContext *context = (SeriesContext *)series_of(httpTask)->get_context();
    context->size = size;
}

void parallelCallback(const ParallelWork *parallel)
{
    cout<<"parallel work"<<endl;
    for(size_t i = 0;i<parallel->size();++i)
    {
        // size()  获取序列的个数
        SeriesContext *context = (SeriesContext *)parallel->series_at(i)->get_context();
        // series_at获取某个序列
        cout<<"url = "<<context->url<<"size = "<<context->size<<endl;
        delete context;
    }
}
int main()
{
    vector<string> vec{
        "http://www.qq.com",
        "http://www.baidu.com",
        "http://www.taobao.com"
    };

    // 创建一个并行任务
    ParallelWork *parallel = Workflow::create_parallel_work(parallelCallback);
    for(int i =0;i<3;i++)
    {
        // 创建一个简单的http任务
        WFHttpTask *httpTask = WFTaskFactory::create_http_task(vec[i],10,0,httpCallback);
        // 创建序列
        SeriesWork *series = Workflow::create_series_work(httpTask,nullptr);
        // 在创建序列之后,申请Context
        SeriesContext *context = new SeriesContext();
        series->set_context(context);
        context->url = vec[i];
        // v把序列加入到并行任务中
        parallel->add_series(series);
    }
    parallel->start(); // 启动parrel
    gWaitGroup.wait();
}

在这里插入图片描述
在这里插入图片描述

)

在这里插入图片描述

给所有序列都添加上回调函数

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Redis处理接口幂等性的两种方案

下游传递唯一请求编号

在这里插入图片描述

防重Token令牌

在这里插入图片描述
在这里插入图片描述

使用Workflow作为HTTP服务端

在这里插入图片描述

在这里插入图片描述

process函数对象

  • 一有请求就去调用process函数,序列是自动创建的
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 简单的使用

#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;
}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}

在这里插入图片描述

  • 第一个服务端

#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;
    // 修改响应
    protocol::HttpResponse *resp = serverTask->get_resp();
    resp->set_http_version("HTTP/1.1");
    resp->set_status_code("200");
    resp->set_reason_phrase("ok");
    resp->set_header_pair("Content-type","text/html");
    // workflow框架会自动加入s首部字段 connection keep-aline
    resp->append_output_body("<html>Hello</html>");
}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}

在这里插入图片描述

  • 响应放到回调函数中
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;

    serverTask->set_callback(
                             [](WFHttpTask * serverTask)
                             {
                             cout<<"Callback is called"<<endl;
                             protocol::HttpResponse *resp = serverTask->get_resp();
                             resp->set_http_version("HTTP/1.1");
                             resp->set_status_code("200");
                             resp->set_reason_phrase("ok");
                             resp->set_header_pair("Content-type","text/html");
                             // workflow框架会自动加入s首部字段 connection keep-aline
                             resp->append_output_body("<html>Hello</html>");
                             }
                            );
    // 修改响应
}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}

在这里插入图片描述

在服务端任务序列中添加任务

登录业务

在这里插入图片描述
在这里插入图片描述

  • 执行顺序的确定

#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void redisCallback(WFRedisTask *serverTask)
{
    cout<<"redisCallback is called"<<endl;
}
void serverTaskCallback(WFHttpTask *serverTask)
{
    cout<<"serverTaskCallback is called"<<endl;
}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;
    // 修改响应

    // 获取r用户名和密码
    protocol::HttpRequest *req = serverTask->get_req();
    string uri = req->get_request_uri();
    string nameKV = uri.substr(0,uri.find("&"));
    string passwdKV = uri.substr(uri.find("&")+1);

    string name = nameKV.substr(nameKV.find("=")+1);
    string passwd = passwdKV.substr(passwdKV.find("=")+1);
    cout<<"name = "<<name<<"  passwd="<<passwd<<endl;

    // 创建Redistask
    WFRedisTask *redisTask=
        WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,redisCallback);
    redisTask->get_req()->set_request("hget",{"54user",name});// 这个任务需要process之后执行
    series_of(serverTask)->push_back(redisTask);
    serverTask->set_callback(serverTaskCallback);
    cout<<"process is returned"<<endl;
}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}

在这里插入图片描述


#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

static WFFacilities::WaitGroup gWaitGroup(1);

struct  SeriesContext
{
    string password;
    protocol::HttpResponse *resp;
};
void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void redisCallback(WFRedisTask *redisTask)
{
    cout<<"redisCallback is called"<<endl;
    protocol::RedisResponse *resp = redisTask->get_resp();
    protocol::RedisValue value;
    resp->get_result(value);

    // 提取共享的数据
    SeriesContext *context = (SeriesContext*)series_of(redisTask)->get_context();
    if(value.string_value() == context->password)
    {
        context->resp->append_output_body("login success");
    }
    else
    {
        context->resp->append_output_body("login failed");
    }
    delete context;
}
void serverTaskCallback(WFHttpTask *serverTask)
{
    cout<<"serverTaskCallback is called"<<endl;
}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;
    // 修改响应

    // 获取r用户名和密码
    protocol::HttpRequest *req = serverTask->get_req();
    string uri = req->get_request_uri();
    string nameKV = uri.substr(0,uri.find("&"));
    string passwdKV = uri.substr(uri.find("&")+1);

    string name = nameKV.substr(nameKV.find("=")+1);
    string passwd = passwdKV.substr(passwdKV.find("=")+1);
    cout<<"name = "<<name<<"  passwd="<<passwd<<endl;

    // 设置要共享的数据
    SeriesContext *context = new SeriesContext;
    context->password = passwd;
    context->resp = serverTask->get_resp();
    series_of(serverTask)->set_context(context);

    // 创建Redistask
    WFRedisTask *redisTask=
        WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,redisCallback);
    redisTask->get_req()->set_request("hget",{"54user",name});// 这个任务需要process之后执行
    series_of(serverTask)->push_back(redisTask);
    serverTask->set_callback(serverTaskCallback);
    cout<<"process is returned"<<endl;
}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}

在这里插入图片描述
在这里插入图片描述

部署静态资源

pread任务和pwrite任务

在这里插入图片描述

#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

struct SeriesContext
{
    protocol::HttpResponse *resp;
    char buf[4096];
};

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void fileIOCallback(WFFileIOTask *preadTask)
{
    cout<<"fileIOCallback is called"<<endl;
    SeriesContext *context = (SeriesContext*)series_of(preadTask)->get_context();
    cout<<"buf = "<<context->buf<<endl;
    context->resp->append_output_body(context->buf,strlen(context->buf));


}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;
    // 修改响应
    protocol::HttpResponse *resp = serverTask->get_resp();
    resp->set_http_version("HTTP/1.1");
    resp->set_status_code("200");
    resp->set_reason_phrase("ok");
    resp->set_header_pair("Content-type","text/html");
    // workflow框架会自动加入s首部字段 connection keep-aline
    resp->append_output_body("<html>Hello</html>");

    protocol::HttpRequest *req = serverTask->get_req();
    string method = req->get_method();
    if(method == "GET")
    {
        int fd = open("postform.html",O_RDONLY);
        SeriesContext *context = new SeriesContext();
        context->resp = resp;
        series_of(serverTask)->set_context(context);
        WFFileIOTask *preadTask = WFTaskFactory::create_pread_task(fd,context->buf,4096,0,fileIOCallback);
        series_of(serverTask)->push_back(preadTask);
    }
    else
    {
        resp->append_output_body("<html>Yes</html>");
    }


}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}
wfrest库
  • 目的是简化workflow的使用
  • 按照这样写,uri路由匹配太多了
    在这里插入图片描述

在这里插入图片描述

项目实战:分块上传功能实现

安装wfrest

在这里插入图片描述

分块上传功能的实现方案

在这里插入图片描述

在这里插入图片描述

接口1:POST /file/mupload/init

在这里插入图片描述

上传ID生成

在这里插入图片描述

分块信息处理

在这里插入图片描述
在这里插入图片描述

接口2:POST /file/mupload/uppart

在这里插入图片描述
在这里插入图片描述

接口3:/file/mupload/complete

在这里插入图片描述
在这里插入图片描述

#include <signal.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
#include <workflow/HttpUtil.h>
#include <wfrest/json.hpp>
#include "UnixHeader.h"
using Json = nlohmann::json;
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int signum)
{
    printf("finish 1 work!\n");
    waitGroup.done();
}
void process(WFHttpTask *serverTask)
{
    // http://192.168.118.128:1234/file/mupload/init
    // http://192.168.118.128:1234/file/mupload/uppart?uploadID=liao1122&chkidx=2
    // http://192.168.118.128:1234/file/mupload/complete?uploadID=liao1122
    auto req = serverTask->get_req();
    auto resp = serverTask->get_resp();
    std::string uri = req->get_request_uri();
    std::string path = uri.substr(0, uri.find("?"));
    std::string query = uri.substr(uri.find("?") + 1);
    std::string method = req->get_method();
    if (path == "/file/mupload/init" && method == "POST")
    {
        // 1 解析请求报文
        const void *body;
        size_t size;
        req->get_parsed_body(&body, &size);
        // 2 从字符串解析成json
        Json fileInfo = Json::parse(static_cast<const char *>(body));
        std::string filename = fileInfo["filename"];
        int filesize = fileInfo["filesize"];
        std::string filehash = fileInfo["filehash"];
        // fprintf(stderr,"%s %s %d\n", filename.c_str(), filehash.c_str(), filesize);
        //  3 初始化分块操作
        //  生成uploadID
        std::string uploadID = "liao";
        time_t now = time(NULL);
        struct tm *ptm = localtime(&now);
        char timeStamp[30] = {0};
        sprintf(timeStamp, "%02d%02d%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
        uploadID = uploadID + timeStamp;
        // fprintf(stderr,"%s\n", uploadID.c_str());
        //  生成分块信息
        int chunkcount;
        int chunksize = 1024 * 1024;
        if (filesize % chunksize != 0)
        {
            chunkcount = filesize / chunksize + 1;
        }
        else
        {
            chunkcount = filesize / chunksize;
        }
        // fprintf(stderr,"chunksize = %d, chunkcount = %d\n", chunksize, chunkcount);
        //  初始化redis
        //  HSET uploadID chunkcount 6
        //  HSET uploadID filehash xxx
        //  HSET uploadID filesize xxx
        std::vector<std::vector<std::string>> argsVec = {
            {uploadID, "chunkcount", std::to_string(chunkcount)},
            {uploadID, "filehash", filehash},
            {uploadID, "filesize", std::to_string(filesize)}};
        for (int i = 0; i < 3; ++i)
        {
            auto redisTask = WFTaskFactory::create_redis_task("redis://localhost", 0, nullptr);
            redisTask->get_req()->set_request("HSET", argsVec[i]);
            redisTask->start();
        }
        // 生成响应给客户端
        Json uppartInfo;
        uppartInfo["status"] = "OK";
        uppartInfo["uploadID"] = uploadID;
        uppartInfo["chunkcount"] = chunkcount;
        uppartInfo["filesize"] = filesize;
        uppartInfo["chunksize"] = chunksize;
        uppartInfo["filehash"] = filehash;
        resp->append_output_body(uppartInfo.dump());
    }
    else if (path == "/file/mupload/uppart" && method == "POST")
    {
        // fprintf(stderr,"uppart\n");
        //  解析uri的查询参数,提取出uploadID和chkidx
        //  query = "uploadID=liao101335&chkidx=2"
        std::string uploadIDKV = query.substr(0, query.find("&"));
        std::string uploadID = uploadIDKV.substr(uploadIDKV.find("=") + 1);
        std::string chkidxKV = query.substr(query.find("&") + 1);
        std::string chkidx = chkidxKV.substr(chkidxKV.find("=") + 1);
        // 保存单个分片 ./filehash/1 filehash需要从redis当中获取 HGET uploadID filehash
        auto redisTaskHGET = WFTaskFactory::create_redis_task("redis://localhost", 0, [chkidx, req](WFRedisTask *redisTaskHGET)
                                                              {
            protocol::RedisValue value;
            redisTaskHGET->get_resp()->get_result(value);
            std::string filehash = value.string_value();
            mkdir(filehash.c_str(),0777);
            std::string filepath = filehash + "/" + chkidx;
            // 文件IO任务
            int fd = open(filepath.c_str(),O_RDWR|O_CREAT,0666);
            const void *body;
            size_t size;
            req->get_parsed_body(&body,&size);
            write(fd,body,size);
            close(fd); });
        redisTaskHGET->get_req()->set_request("HGET", {uploadID, "filehash"});
        series_of(serverTask)->push_back(redisTaskHGET);
        // 记录下载进度 HSET uploadID chkidx 1
        auto redisTaskHSET = WFTaskFactory::create_redis_task("redis://localhost", 0, nullptr);
        redisTaskHSET->get_req()->set_request("HSET", {uploadID, "chkidx_" + chkidx, "1"});
        series_of(serverTask)->push_back(redisTaskHSET);

        // 设置响应的内容
        resp->append_output_body("OK");
    }
    else if (path == "/file/mupload/complete" && method == "POST")
    {
        // fprintf(stderr,"complete\n");
        // 解析请求,提取出uploadID
        std::string uploadID = query.substr(query.find("=") + 1);
        // 查询上传进度
        //  HGETALL uploadID
        //  chunkcount键所对应的值-->下载的总进度
        //  前缀为chkidx_的键的数量-->下载的当前进度
        auto redisTask = WFTaskFactory::create_redis_task("redis://localhost", 0, [resp](WFRedisTask *redisTask)
                                                          {
            protocol::RedisValue value;
            redisTask->get_resp()->get_result(value);
            int chunkcount;
            int chunknow = 0;
            for(int i = 0; i < value.arr_size(); i+=2){
                std::string key = value.arr_at(i).string_value();
                std::string val = value.arr_at(i+1).string_value();
                if(key == "chunkcount"){
                    chunkcount = std::stoi(val);
                }
                else if(key.substr(0,7) == "chkidx_"){
                    ++chunknow;
                }
            }
            //fprintf(stderr,"chunkcount = %d, chunknow = %d\n", chunkcount, chunknow);
            if(chunkcount == chunknow){
                //分块合并操作
                resp->append_output_body("SUCCESS");
            }
            else{
                resp->append_output_body("FAIL");
            } });
        redisTask->get_req()->set_request("HGETALL", {uploadID});
        series_of(serverTask)->push_back(redisTask);
    }
}
int main()
{
    signal(SIGINT, sighandler);
    WFHttpServer server(process);
    if (server.start(1234) == 0)
    {
        waitGroup.wait();
        server.stop();
    }
    else
    {
        perror("server start error!\n");
        exit(-1);
    }
    return 0;
}

MySQL任务

workflow去操作表的时候,表的名字前面一定要加 数据库名.
在这里插入图片描述

  • 注意是在做客户端

在这里插入图片描述

#include <54func.h>
#include <iostream>
#include <vector>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/MySQLMessage.h>
#include <workflow/MySQLResult.h>
#include <workflow/MySQLUtil.h>
static WFFacilities::WaitGroup waitGroup(1);
void sigHandler(int num)
{
    waitGroup.done();
    printf("waitGroup is done\n");
}
void mysqlCallback(WFMySQLTask *mysqlTask)
{
    // step 1 检查连接的状态
    if (mysqlTask->get_state() != WFT_STATE_SUCCESS)
    {
        std::cerr << "error msg :" << WFGlobal::get_error_string(mysqlTask->get_state(), mysqlTask->get_error());
        return;
    }
    protocol::MySQLResponse *resp = mysqlTask->get_resp();
    protocol::MySQLResultCursor cursor(mysqlTask->get_resp());
    // step 2 检查mysql任务的状态
    if (resp->get_packet_type() == MYSQL_PACKET_ERROR)
    {
        std::cerr << "ERROR, error code = " << resp->get_error_code()
                  << " msg = " << resp->get_error_msg();
        return;
    }
    do
    { // step 3 遍历结果集合
      // step 4 判断指令的类型是 读 select 写 update insert delete from alter
        if (cursor.get_cursor_status() == MYSQL_STATUS_OK)
        {
            // 说明是写操作
            std::cerr << "OK, " << cursor.get_affected_rows() << " rows affected."
                      << cursor.get_warnings() << " warnings. insert_id = " << cursor.get_insert_id() << ".\n";
        }
        else if (cursor.get_cursor_status() == MYSQL_STATUS_GET_RESULT)
        {
            // 说明sql语句是读操作
            std::cerr << "field count = " << cursor.get_field_count()
                      << " rows count = " << cursor.get_rows_count() << "\n";
            // 找到所有域的信息
            const protocol::MySQLField *const *fields = cursor.fetch_fields();
            for (int i = 0; i < cursor.get_field_count(); ++i)
            {
                std::cerr << "database = " << fields[i]->get_db()
                          << " table = " << fields[i]->get_table()
                          << " fieldname = " << fields[i]->get_name()
                          << " fieldtype = " << datatype2str(fields[i]->get_data_type())
                          << "\n";
            }
            // 取出表格当中的所有内容
            std::vector<std::vector<protocol::MySQLCell>> rows;
            cursor.fetch_all(rows);
            for (unsigned int j = 0; j < rows.size(); ++j)
            {
                for (unsigned int i = 0; i < rows[j].size(); ++i)
                {
                    if (rows[j][i].is_int())
                    {
                        std::cerr << rows[j][i].as_int() << " ";
                    }
                    else if (rows[j][i].is_string())
                    {
                        std::cerr << rows[j][i].as_string() << " ";
                    }
                    else if (rows[j][i].is_datetime())
                    {
                        std::cerr << rows[j][i].as_datetime() << " ";
                    }
                }
                std::cerr << "\n";
            }
        }
    } while (cursor.next_result_set()); // 多个sql指令合在一起的情况
    //cursor.next_result_set(); // 当执行多个sql语句时
}
int main()
{
    signal(SIGINT, sigHandler);
    WFMySQLTask *mysqlTask =
        WFTaskFactory::create_mysql_task("mysql://root:123@127.0.0.1:3306", 0, mysqlCallback);
    // std::string sql = "update cloudisk.tbl_user set email_validated = 1 where
    // std::string sql = "show databases;";
    //std::string sql = "select * from cloudisk.tbl_user_token;";
    //std::string sql = "INSERT INTO cloudisk.tbl_user_token  VALUES(1, 'liao', 'abcdef');";
    // workflow去操作表的时候,表的名字前面一定要加 数据库名.

    std::string sql = "select * from cloudisk.tbl_user_token;";
    sql += "INSERT INTO cloudisk.tbl_user_token  VALUES(3, 'liao3', 'abcdef');";
    auto req = mysqlTask->get_req();
    req->set_query(sql);
    mysqlTask->start();
    waitGroup.wait();
}

Mysql可以执行多个语句,redis一次只能执行一个任务

定时任务

在这里插入图片描述

#include <54func.h>
#include <iostream>
#include <map>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <wfrest/HttpServer.h>
static WFFacilities::WaitGroup waitGroup(1);
void sigHandler(int num)
{
    waitGroup.done();
    printf("waitGroup is done\n");
}
void timerCallback(WFTimerTask *timerTask)
{
    std::cerr << "timer callback " << time(NULL) << std::endl;
    WFTimerTask *nextTask =
        WFTaskFactory::create_timer_task(5000000, timerCallback);
    series_of(timerTask)->push_back(nextTask);
}
int main()
{
    signal(SIGINT, sigHandler);
    WFTimerTask *timerTask =
        WFTaskFactory::create_timer_task(5000000, timerCallback);
    timerTask->start();
    std::cerr << "timer start " << time(NULL) << std::endl;
    waitGroup.wait();
    return 0;
}

在这里插入图片描述

文件IO任务

文件IO任务

在这里插入图片描述

示例:静态资源服务器

在这里插入图片描述

pread任务和pwrite任务

在这里插入图片描述

#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>

using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;

struct SeriesContext
{
    protocol::HttpResponse *resp;
    char buf[4096];
};

static WFFacilities::WaitGroup gWaitGroup(1);

void handler(int signum)
{
        printf("signum = %d\n",signum);
    gWaitGroup.done();
}

void fileIOCallback(WFFileIOTask *preadTask)
{
    cout<<"fileIOCallback is called"<<endl;
    SeriesContext *context = (SeriesContext*)series_of(preadTask)->get_context();
    cout<<"buf = "<<context->buf<<endl;
    context->resp->append_output_body(context->buf,strlen(context->buf));


}

void process(WFHttpTask *serverTask)
{
    cout<<"Process is called"<<endl;
    // 修改响应
    protocol::HttpResponse *resp = serverTask->get_resp();
    resp->set_http_version("HTTP/1.1");
    resp->set_status_code("200");
    resp->set_reason_phrase("ok");
    resp->set_header_pair("Content-type","text/html");
    // workflow框架会自动加入s首部字段 connection keep-aline
    resp->append_output_body("<html>Hello</html>");

    protocol::HttpRequest *req = serverTask->get_req();
    string method = req->get_method();
    if(method == "GET")
    {
        int fd = open("postform.html",O_RDONLY);
        SeriesContext *context = new SeriesContext();
        context->resp = resp;
        series_of(serverTask)->set_context(context);
        WFFileIOTask *preadTask = WFTaskFactory::create_pread_task(fd,context->buf,4096,0,fileIOCallback);
        series_of(serverTask)->push_back(preadTask);
    }
    else
    {
        resp->append_output_body("<html>Yes</html>");
    }


}

int main()
{
   signal(SIGINT,handler);
   WFHttpServer server(process);
   server.start(1234); // listen 1234端口
   gWaitGroup.wait();
}

wfrest的使用

在这里插入图片描述


#include <54func.h>
#include <iostream>
using std::cout;
using std::endl;
#include <workflow/WFFacilities.h>
#include <wfrest/HttpServer.h>
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int num)
{
    printf("sig %d is coming\n", num);
    // 将计数器的值减1
    waitGroup.done();
}
int main()
{
    using std::string;
    signal(SIGINT, sighandler);
    using namespace wfrest;
    HttpServer httpserver;
    httpserver.GET("/wfrest/test0",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       // 使用workflow的方式设置响应
                       resp->append_output_body("hello, wfrest client");
                   });
    httpserver.GET("/wfrest/query",
                   [](const HttpReq *req, HttpResp *resp)
                   {
                       // uri的查询参数部分
                       cout << "username:" << req->query("username") << endl;
                       auto queryList = req->query_list();
                       for (auto &query : queryList)
                       {
                           cout << query.first << ": " << query.second << endl;
                       }
                       resp->append_output_body("hello, wfrest client1");
                   });
    httpserver.POST("/wfrest/body",
                    [](const HttpReq *req, HttpResp *resp)
                    {
                        // 请求体
                        string body = req->body();
                        cout << "body:" << body << endl;
                        resp->append_output_body("hello, wfrest client");
                    });
    httpserver.POST("/wfrest/urlencoded",
                    [](const HttpReq *req, HttpResp *resp)
                    {
                        // URLENCODED类型的请求体
                        if (req->content_type() == APPLICATION_URLENCODED)
                        {
                            auto formKV = req->form_kv();
                            for (auto &elem : formKV)
                            {
                                cout << elem.first << ": " << elem.second << endl;
                            }
                        }
                        resp->append_output_body("hello, wfrest client");
                    });
    httpserver.POST("/wfrest/formdata",
                    [](const HttpReq *req, HttpResp *resp)
                    {
                        // form_data类型的请求体
                        if (req->content_type() == MULTIPART_FORM_DATA)
                        {
                            auto form = req->form();
                            for (auto &elem : form)
                            {
                                cout << elem.first << ": " << elem.second.first
                                     << endl;
                                cout << elem.second.second << endl;
                            }
                        }
                        resp->append_output_body("hello, wfrest client");
                    });
    if (httpserver.track().start(1234) == 0)
    {
        // 列出当前服务器上部署的服务
        httpserver.list_routes();
        waitGroup.wait();
        httpserver.stop();
    }
    else
    {
        printf("HttpServer start failed.\n");
    }
    return 0;
}
  • 更多功能

简化首部字段操作

resp->add_header_pair()    // 添加首部字段
resp_set_header_pair() // 首部字段已经存在

resp->headers["Location"] = ""; // 没有就添加有就修改

简化查询参数操作

在这里插入图片描述

  • 简化登录业务
    在这里插入图片描述

#include <54func.h>
#include <iostream>
using std::cout;
using std::endl;
#include <workflow/WFFacilities.h>
#include <workflow/MySQLResult.h>
#include <workflow/MySQLUtil.h>
#include <wfrest/HttpServer.h>
#include <wfrest/json.hpp>
using std::vector;
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int num)
{
    printf("sig %d is coming\n", num);
    // 将计数器的值减1
    waitGroup.done();
}
int main()
{
    using std::string;
    signal(SIGINT, sighandler);
    using namespace wfrest;
    HttpServer httpserver;
    // String可以自动设置首部字段
    httpserver.GET("/wfrest/test0",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       resp->String("hello,wfrest client2");
                   });
    // 部署静态资源
    httpserver.GET("/wfrest/file",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       resp->File("postform.html");
                   });
    // 响应JSON
    httpserver.GET("/wfrest/json",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       Json data;
                       data["username"] = "liubei";
                       data["password"] = "123";
                       resp->Json(data);
                   });
    // MySQL默认结果
    httpserver.GET("/wfrest/mysql0",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       string url("mysql://root:123@localhost");
                       string sql("select * from mycloud.tbl_user_token");
                       resp->MySQL(url, sql);
                   });
    // MySQL的Json结果
    httpserver.GET("/wfrest/mysql1",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       string url("mysql://root:123@localhost");
                       string sql("select * from mycloud.tbl_user_token");
                       // json指针指向的就是MySQL的结果集
                       resp->MySQL(url, sql, [resp](Json *json)
                                   {
string msg = (*json)["result_set"][0]["rows"][0][1];
resp->String(msg); });
                   });
    // MySQL cursor
    httpserver.GET("/wfrest/mysql2",
                   [](const HttpReq *, HttpResp *resp)
                   {
                       string url("mysql://root:123@localhost");
                       string sql("select * from mycloud.tbl_user_token");
                       using namespace protocol;
                       // pcursor指针指向的就是MySQL的结果集的迭代器
                       resp->MySQL(url, sql, [resp](MySQLResultCursor *pcursor)
                                   {
std::vector<std::vector<MySQLCell>> matrix;
pcursor->fetch_all(matrix);
string msg = matrix[0][2].as_string();
resp->String(msg); });
                   });
    // 在wfrest中显式使用序列
    httpserver.GET("/wfrest/series",
                   [](const HttpReq *, HttpResp *resp, SeriesWork *series)
                   {
                       auto timerTask = WFTaskFactory::create_timer_task(1000 *
                                                                             1000,
                                                                         [resp](WFTimerTask *)
                                                                         {
                                                                             printf("timerCallback is running.\n");
                                                                             resp->String("timer callback");
                                                                         });
                       series->push_back(timerTask);
                   });
    // 序列配合mysql任务
    httpserver.GET("/wfrest/mysql3",
                   [](const HttpReq *, HttpResp *httpResp, SeriesWork *series)
                   {
                       string url("mysql://root:123@localhost");
                       auto mysqlTask =
                           WFTaskFactory::create_mysql_task(url, 1,
                                                            [httpResp](WFMySQLTask *mysqltask)
                                                            {
                                                                // 0. 对任务的状态进行检测
                                                                int state = mysqltask->get_state();
                                                                int error = mysqltask->get_error();
                                                                if (state != WFT_STATE_SUCCESS)
                                                                {
                                                                    printf("%s\n", WFGlobal::get_error_string(state,
                                                                                                              error));
                                                                    return;
                                                                }
                                                                // 1. 检测SQL语句是否存在语法错误
                                                                auto resp = mysqltask->get_resp();
                                                                if (resp->get_packet_type() == MYSQL_PACKET_ERROR)
                                                                {
                                                                    printf("ERROR %d: %s\n", resp->get_error_code(),
                                                                           resp->get_error_msg().c_str());
                                                                    return;
                                                                }
                                                                printf("sql sentence is ok.\n");
                                                                using namespace protocol;
                                                                MySQLResultCursor cursor(resp);
                                                                if (cursor.get_cursor_status() == MYSQL_STATUS_OK)
                                                                {
                                                                    printf("Query OK. %llu row affected.\n",
                                                                           cursor.get_affected_rows());
                                                                }
                                                                else if (cursor.get_cursor_status() ==
                                                                         MYSQL_STATUS_GET_RESULT)
                                                                {
                                                                    // 读操作
                                                                    vector<vector<MySQLCell>> matrix;
                                                                    cursor.fetch_all(matrix);
                                                                    string msg = matrix[1][1].as_string();
                                                                    httpResp->String(msg);
                                                                }
                                                            });
                       string sql("select * from mycloud.tbl_user_token");
                       mysqlTask->get_req()->set_query(sql);
                       series->push_back(mysqlTask);
                   });
    if (httpserver.track().start(1234) == 0)
    {
        // 列出当前服务器上部署的服务
        httpserver.list_routes();
        waitGroup.wait();
        httpserver.stop();
    }
    else
    {
        printf("HttpServer start failed.\n");
    }
    return 0;
}

wfrest解析报文体

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

解析x-www-form-urlencoded(传简单消息)

#include <54func.h>
#include <iostream>
#include<map>
using std::cout;
using std::endl;
using std::cerr;
using std::map;
#include <workflow/WFFacilities.h>
#include <wfrest/HttpServer.h>
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int num)
{
    printf("sig %d is coming\n", num);
    // 将计数器的值减1
    waitGroup.done();
}
int main()
{
    using std::string;
    using namespace wfrest;
    signal(SIGINT, sighandler);
    wfrest::HttpServer server;
    server.GET("/abc",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
               resp->headers["key"] = "value";
               });
    server.POST("/def",[](const HttpReq *req,HttpResp *resp){
                // 头体: a=b&c=d&e=f
                if(req->content_type() == APPLICATION_URLENCODED)
                {
                    map<string,string> body = req->form_kv();
                    for(auto &myPair:body)
                    {
                        cerr<<"key = "<<myPair.first<<",value = "<<myPair.second<<endl;
                    }
                }
                });
    if (server.start(1234) == 0)
    {
        // 列出当前服务器上部署的服务
        server.list_routes();
        waitGroup.wait();
        server.stop();
    }
    else
    {
        printf("HttpServer start failed.\n");
    }
    return 0;
}

解析from-data(传文件)

在这里插入图片描述

// 注意框架,要求单个文件不能上传超过128M

// 会下载
    server.POST("/upload",[](const HttpReq * req,HttpResp *resp){
                  resp->File("./upload.html");
                    if(req->content_type() == MULTIPART_FORM_DATA)
                    {
                        map<string,pair<string,string>> &formdata = req->form();
                        for(auto &mypair : formdata)
                        {
                            cerr<<"key = "<<mypair.first
                            <<",value1 = "<<mypair.second.first
                            <<",value2 = "<<mypair.second.second<<endl;
                        }
                    }
               });
    // z只会展示
    server.GET("/upload",[](const HttpReq * req,HttpResp *resp){
                  resp->File("./upload.html");
                  });
                  server.Static("/static","./static_res");

在这里插入图片描述

部署静态资源

server.GET("/postform.html",[](const HttpReq *req,HttpResp *resp){
               resp->File("postform.html");
               });

在这里插入图片描述

  • 一次性部署多个文件
    在这里插入图片描述
    在这里插入图片描述
int main()
{
    using std::string;
    using namespace wfrest;
    signal(SIGINT, sighandler);
    wfrest::HttpServer server;
    server.GET("/abc",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
               resp->headers["key"] = "value";
               });
    server.POST("/def",[](const HttpReq *req,HttpResp *resp){
                // 头体: a=b&c=d&e=f
                if(req->content_type() == APPLICATION_URLENCODED)
                {
                    map<string,string> body = req->form_kv();
                    for(auto &myPair:body)
                    {
                        cerr<<"key = "<<myPair.first<<",value = "<<myPair.second<<endl;
                    }
                }
                });
    server.GET("/postform.html",[](const HttpReq *req,HttpResp *resp){
               resp->File("postform.html");
               });
    server.Static("/static","./static_res");
    if (server.start(1234) == 0)
    {
        // 列出当前服务器上部署的服务
        server.list_routes();
        waitGroup.wait();
        server.stop();
    }
    else
    {
        printf("HttpServer start failed.\n");
    }
    return 0;
}

解析json报文体格式

#include<wfrest/json.hpp>
using Json = nlohmann::json;
    server.POST("/json", [](const HttpReq *req, HttpResp *resp) {
                try {
                string body = req->body();
                Json info = Json::parse(body);
                string filename = info["filename"];
                string filecontent = info["filecontent"];
                int size = info["filesize"];
                cerr << filename << filecontent << size << endl;
                resp->String("OK"); // 添加响应,以便客户端知道请求成功
                } catch (const Json::parse_error &e) {
                cerr << "JSON parse error: " << e.what() << endl;
                resp->String("Invalid JSON format");
                } catch (const Json::out_of_range &e) {
                cerr << "JSON key error: " << e.what() << endl;
                resp->String("Missing required fields in JSON");
                }
                });// 删除其中一个定义

发送SQL

直接将结果返给前端

server.GET("/mysql",[](const HttpReq *res,HttpResp *resp){
                             resp->MySQL("mysql://root:123@localhost","select * from cloudisk.tbl_user_token");
                             });

在这里插入图片描述

使用三参数版本,调用序列进行回调

server.GET("/mysql1", [](const HttpReq *req, HttpResp *resp, SeriesWork *series)
               { WFMySQLTask *mysqlTask =
                     WFTaskFactory::create_mysql_task("mysql://root:123@localhost", 0,
                                                      [](WFMySQLTask *mysqlTask)
                                                      {
                                                          // step 1 检查连接的状态
                                                          if (mysqlTask->get_state() != WFT_STATE_SUCCESS)
                                                          {
                                                              std::cerr << "error msg :" << WFGlobal::get_error_string(mysqlTask->get_state(), mysqlTask->get_error());
                                                              return;
                                                          }
                                                          protocol::MySQLResponse *resp = mysqlTask->get_resp();
                                                          protocol::MySQLResultCursor cursor(mysqlTask->get_resp());
                                                          // step 2 检查mysql任务的状态
                                                          if (resp->get_packet_type() == MYSQL_PACKET_ERROR)
                                                          {
                                                              std::cerr << "ERROR, error code = " << resp->get_error_code()
                                                                        << " msg = " << resp->get_error_msg();
                                                              return;
                                                          }
                                                          do
                                                          { // step 3 遍历结果集合
                                                              // step 4 判断指令的类型是 读 select 写 update insert delete from alter
                                                              if (cursor.get_cursor_status() == MYSQL_STATUS_OK)
                                                              {
                                                                  // 说明是写操作
                                                                  std::cerr << "OK, " << cursor.get_affected_rows() << " rows affected."
                                                                            << cursor.get_warnings() << " warnings. insert_id = " << cursor.get_insert_id() << ".\n";
                                                              }
                                                              else if (cursor.get_cursor_status() == MYSQL_STATUS_GET_RESULT)
                                                              {
                                                                  // 说明sql语句是读操作
                                                                  std::cerr << "field count = " << cursor.get_field_count()
                                                                            << " rows count = " << cursor.get_rows_count() << "\n";
                                                                  // 找到所有域的信息
                                                                  const protocol::MySQLField *const *fields = cursor.fetch_fields();
                                                                  for (int i = 0; i < cursor.get_field_count(); ++i)
                                                                  {
                                                                      std::cerr << "database = " << fields[i]->get_db()
                                                                                << " table = " << fields[i]->get_table()
                                                                                << " fieldname = " << fields[i]->get_name()
                                                                                << " fieldtype = " << datatype2str(fields[i]->get_data_type())
                                                                                << "\n";
                                                                  }
                                                                  // 取出表格当中的所有内容
                                                                  std::vector<std::vector<protocol::MySQLCell>> rows;
                                                                  cursor.fetch_all(rows);
                                                                  for (unsigned int j = 0; j < rows.size(); ++j)
                                                                  {
                                                                      for (unsigned int i = 0; i < rows[j].size(); ++i)
                                                                      {
                                                                          if (rows[j][i].is_int())
                                                                          {
                                                                              std::cerr << rows[j][i].as_int() << " ";
                                                                          }
                                                                          else if (rows[j][i].is_string())
                                                                          {
                                                                              std::cerr << rows[j][i].as_string() << " ";
                                                                          }
                                                                          else if (rows[j][i].is_datetime())
                                                                          {
                                                                              std::cerr << rows[j][i].as_datetime() << " ";
                                                                          }
                                                                      }
                                                                      std::cerr << "\n";
                                                                  }
                                                              }
                                                          } while (cursor.next_result_set()); // 多个sql指令合在一起的情况
                                                          // cursor.next_result_set(); // 当执行多个sql语句时

                                                      });

        mysqlTask->get_req()->set_query("select * from cloudisk.tbl_user_token");
        series->push_back(mysqlTask); });

在这里插入图片描述

import sys import json import os from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QListWidget, QLabel, QDialog, QLineEdit, QTextEdit, QComboBox, QMessageBox, QTabWidget, QToolBar, QAction, QTreeWidget, QTreeWidgetItem, QSplitter, QStatusBar, QToolButton, QStackedWidget, QDateEdit, QListWidgetItem, QCheckBox, QFormLayout) from PyQt5.QtCore import Qt, QSize, QDate from PyQt5.QtGui import QIcon, QFont, QColor, QPalette, QBrush, QLinearGradient class WorkflowManager(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("高级工作流程管理器 - 专业版") self.resize(1200, 800) self.workflows = [] self.current_workflow = None self.current_stage_index = 0 self.load_workflows() # 设置主窗口样式 - 现代专业配色方案 self.setStyleSheet(""" QMainWindow { background-color: #f8f9fa; font-family: 'Segoe UI', 'Arial', sans-serif; } QToolBar { background-color: #ffffff; border-bottom: 1px solid #e9ecef; padding: 5px; spacing: 10px; } QPushButton { background-color: #4e73df; color: white; border-radius: 6px; padding: 8px 16px; min-width: 90px; font-size: 14px; font-weight: 500; border: none; transition: background-color 0.3s; } QPushButton:hover { background-color: #2e59d9; } QPushButton:pressed { background-color: #1a3bb3; } QListWidget, QTreeWidget { background-color: white; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 14px; padding: 5px; } QTabWidget::pane { border: 1px solid #e0e0e0; border-radius: 8px; background: white; margin-top: 5px; } QTabBar::tab { background: #f8f9fa; border: 1px solid #e0e0e0; border-bottom: none; padding: 10px 20px; font-size: 14px; color: #495057; border-top-left-radius: 6px; border-top-right-radius: 6px; margin-right: 2px; } QTabBar::tab:selected { background: white; border-bottom-color: white; font-weight: 600; color: #4e73df; } QTabBar::tab:hover { background: #e9ecef; } QLabel { font-size: 14px; color: #495057; } QLineEdit, QTextEdit, QComboBox { font-size: 14px; padding: 8px; background-color: white; color: #495057; border: 1px solid #ced4da; border-radius: 4px; } QLineEdit:focus, QTextEdit:focus { border: 1px solid #4e73df; } QCheckBox { font-size: 14px; color: #495057; spacing: 8px; } QStatusBar { background-color: #ffffff; color: #495057; font-size: 13px; border-top: 1px solid #e9ecef; } """) # 设置应用字体 app_font = QFont("Segoe UI", 10) QApplication.setFont(app_font) # 创建主界面 self.create_main_ui() # 加载默认工作流程 if self.workflows: self.current_workflow = self.workflows[0] self.update_workflow_display() def create_main_ui(self): """创建主界面""" # 创建工具栏 toolbar = QToolBar("主工具栏") toolbar.setIconSize(QSize(24, 24)) toolbar.setMovable(False) self.addToolBar(toolbar) # 添加工具栏动作 new_workflow_action = QAction(QIcon.fromTheme("document-new"), "新建流程", self) new_workflow_action.triggered.connect(self.new_workflow) toolbar.addAction(new_workflow_action) edit_workflow_action = QAction(QIcon.fromTheme("document-edit"), "编辑流程", self) edit_workflow_action.triggered.connect(self.edit_workflow) toolbar.addAction(edit_workflow_action) delete_workflow_action = QAction(QIcon.fromTheme("edit-delete"), "删除流程", self) delete_workflow_action.triggered.connect(self.delete_workflow) toolbar.addAction(delete_workflow_action) save_action = QAction(QIcon.fromTheme("document-save"), "保存", self) save_action.triggered.connect(self.save_workflows) toolbar.addAction(save_action) toolbar.addSeparator() stats_action = QAction(QIcon.fromTheme("view-statistics"), "统计", self) stats_action.triggered.connect(self.show_stats) toolbar.addAction(stats_action) # 创建主内容区域 main_widget = QWidget() self.setCentralWidget(main_widget) main_layout = QHBoxLayout(main_widget) main_layout.setContentsMargins(15, 15, 15, 15) main_layout.setSpacing(15) # 左侧工作流程列表 left_panel = QWidget() left_panel.setFixedWidth(280) left_layout = QVBoxLayout(left_panel) left_layout.setContentsMargins(0, 0, 0, 0) workflow_label = QLabel("工作流程") workflow_label.setStyleSheet("font-size: 16px; font-weight: 600; color: #4e73df; margin-bottom: 8px;") left_layout.addWidget(workflow_label) self.workflow_list = QTreeWidget() self.workflow_list.setHeaderLabel("") self.workflow_list.setRootIsDecorated(False) self.workflow_list.itemClicked.connect(self.select_workflow) left_layout.addWidget(self.workflow_list) main_layout.addWidget(left_panel) # 右侧主内容区 right_splitter = QSplitter(Qt.Vertical) # 工作流程可视化区 self.flow_visualization = QWidget() self.flow_visualization.setStyleSheet("background-color: white; border-radius: 8px; padding: 15px;") flow_viz_layout = QVBoxLayout(self.flow_visualization) self.flow_title = QLabel() self.flow_title.setStyleSheet("font-size: 20px; font-weight: 600; color: #4e73df; margin-bottom: 15px;") flow_viz_layout.addWidget(self.flow_title) # 添加滚动区域 scroll_widget = QWidget() scroll_layout = QHBoxLayout(scroll_widget) scroll_layout.setContentsMargins(0, 0, 0, 0) self.flow_stages = QHBoxLayout() self.flow_stages.setSpacing(15) scroll_layout.addLayout(self.flow_stages) flow_viz_layout.addWidget(scroll_widget) # 添加流程控制按钮 self.flow_control = QHBoxLayout() self.flow_control.setAlignment(Qt.AlignCenter) self.flow_control.setSpacing(20) self.prev_stage_btn = QPushButton("◀ 上一阶段") self.prev_stage_btn.setIcon(QIcon.fromTheme("go-previous")) self.prev_stage_btn.setStyleSheet("min-width: 120px;") self.prev_stage_btn.clicked.connect(self.prev_stage) self.next_stage_btn = QPushButton("下一阶段 ▶") self.next_stage_btn.setIcon(QIcon.fromTheme("go-next")) self.next_stage_btn.setStyleSheet("min-width: 120px;") self.next_stage_btn.clicked.connect(self.next_stage) self.flow_control.addWidget(self.prev_stage_btn) self.flow_control.addWidget(self.next_stage_btn) flow_viz_layout.addLayout(self.flow_control) right_splitter.addWidget(self.flow_visualization) # 成果物管理区 self.artifacts_management = QTabWidget() self.artifacts_management.setStyleSheet(""" QTabWidget::pane { border: none; } """) # 成果物检查 self.artifacts_check = QWidget() self.artifacts_check.setStyleSheet("background-color: white; border-radius: 8px; padding: 15px;") artifacts_layout = QVBoxLayout(self.artifacts_check) self.artifacts_label = QLabel("成果物检查") self.artifacts_label.setStyleSheet("font-size: 18px; font-weight: 600; color: #4e73df; margin-bottom: 10px;") artifacts_layout.addWidget(self.artifacts_label) self.artifacts_list = QListWidget() self.artifacts_list.setStyleSheet("border-radius: 6px;") artifacts_layout.addWidget(self.artifacts_list) # 成果物状态说明 self.status_info = QLabel("") self.status_info.setStyleSheet(""" font-size: 14px; font-weight: 500; color: #6c757d; background-color: #f8f9fa; padding: 10px; border-radius: 6px; margin-top: 10px; """) artifacts_layout.addWidget(self.status_info) self.artifacts_management.addTab(self.artifacts_check, "成果物检查") # 统计 self.stats_widget = QWidget() self.stats_widget.setStyleSheet("background-color: white; border-radius: 8px; padding: 15px;") stats_layout = QVBoxLayout(self.stats_widget) self.stats_label = QLabel("工作流程统计信息将显示在这里") self.stats_label.setStyleSheet("font-size: 14px;") stats_layout.addWidget(self.stats_label) self.artifacts_management.addTab(self.stats_widget, "统计") right_splitter.addWidget(self.artifacts_management) main_layout.addWidget(right_splitter, 1) # 状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage("就绪") def load_workflows(self): """加载工作流程""" if os.path.exists("workflows.json"): with open("workflows.json", "r") as f: self.workflows = json.load(f) else: # 创建默认工作流程 - 使用更协调的颜色方案 default_workflow = { "name": "软件开发变更流程", "description": "标准的软件开发变更管理流程", "stages": [ {"name": "变更点分析", "description": "分析客户提供的变更需求", "color": "#4e73df", "artifacts": ["需求分析文档"], "completed": False}, {"name": "变更点差分", "description": "识别变更前后的差异", "color": "#1cc88a", "artifacts": ["差异分析报告"], "completed": False}, {"name": "设计书修改", "description": "更新设计文档", "color": "#36b9cc", "artifacts": ["更新后的设计文档"], "completed": False}, {"name": "代码修改", "description": "实施代码变更", "color": "#f6c23e", "artifacts": ["变更后的代码"], "completed": False}, {"name": "代码验证", "description": "测试变更代码", "color": "#e74a3b", "artifacts": ["测试报告"], "completed": False}, {"name": "代码审核", "description": "进行代码审查", "color": "#6f42c1", "artifacts": ["代码审查意见"], "completed": False}, {"name": "代码集成", "description": "合并到主分支", "color": "#fd7e14", "artifacts": ["集成后的代码"], "completed": False}, {"name": "成果物输出", "description": "生成最终交付物", "color": "#20c997", "artifacts": ["最终交付包"], "completed": False} ], "tasks": [] } self.workflows.append(default_workflow) self.save_workflows() def save_workflows(self): """保存工作流程""" with open("workflows.json", "w") as f: json.dump(self.workflows, f, indent=4) self.status_bar.showMessage("工作流程已保存", 3000) def update_workflow_display(self): """更新工作流程显示""" if not self.current_workflow: return # 更新工作流程列表 self.workflow_list.clear() for workflow in self.workflows: item = QTreeWidgetItem(self.workflow_list) item.setText(0, workflow["name"]) item.setFont(0, QFont("Segoe UI", 11)) item.setData(0, Qt.UserRole, workflow) # 高亮显示当前选中的工作流程 if workflow == self.current_workflow: item.setBackground(0, QColor("#e9ecef")) # 更新流程标题 self.flow_title.setText(f"{self.current_workflow['name']} - {self.current_workflow['description']}") # 更新流程阶段显示 self.clear_flow_stages() for i, stage in enumerate(self.current_workflow["stages"]): stage_widget = self.create_stage_widget(stage, i) self.flow_stages.addWidget(stage_widget) # 更新按钮状态 self.update_buttons_state() def clear_flow_stages(self): """清空流程阶段显示""" while self.flow_stages.count(): item = self.flow_stages.takeAt(0) if item.widget(): item.widget().deleteLater() def create_stage_widget(self, stage, index): """创建单个阶段显示组件""" stage_widget = QWidget() stage_widget.setFixedWidth(200) # 根据阶段状态设置样式 if index == self.current_stage_index: # 当前阶段 - 高亮显示 stage_widget.setStyleSheet(f""" QWidget {{ background-color: {stage['color']}20; border: 2px solid {stage['color']}; border-radius: 10px; padding: 15px; }} QLabel {{ color: {stage['color']}; }} """) elif index < self.current_stage_index: # 已完成阶段 - 灰色显示 stage_widget.setStyleSheet(f""" QWidget {{ background-color: #e9ecef; border: 2px solid #ced4da; border-radius: 10px; padding: 15px; }} QLabel {{ color: #6c757d; }} """) else: # 未开始阶段 - 默认样式 stage_widget.setStyleSheet(f""" QWidget {{ background-color: #f8f9fa; border: 2px solid #e9ecef; border-radius: 10px; padding: 15px; }} QLabel {{ color: #6c757d; }} """) layout = QVBoxLayout(stage_widget) layout.setSpacing(8) title = QLabel(stage["name"]) title.setStyleSheet("font-weight: 600; font-size: 16px;") layout.addWidget(title) desc = QLabel(stage["description"]) desc.setWordWrap(True) desc.setStyleSheet("font-size: 13px;") layout.addWidget(desc) # 显示阶段状态 status_text = "已完成" if stage.get("completed", False) else "进行中" if index == self.current_stage_index else "未开始" status = QLabel(f"状态: {status_text}") status.setStyleSheet("font-size: 12px; font-style: italic; margin-top: 8px;") layout.addWidget(status) # 添加阶段选择按钮 select_btn = QPushButton("选择此阶段") select_btn.setStyleSheet(f""" QPushButton {{ background-color: {stage['color']}; color: white; font-weight: 500; margin-top: 10px; }} QPushButton:hover {{ background-color: {self.darken_color(stage['color'], 20)}; }} """) select_btn.clicked.connect(lambda _, i=index: self.select_stage(i)) layout.addWidget(select_btn) return stage_widget def darken_color(self, hex_color, percent): """使颜色变暗指定百分比""" r = int(int(hex_color[1:3], 16) * (100 - percent) / 100) g = int(int(hex_color[3:5], 16) * (100 - percent) / 100) b = int(int(hex_color[5:7], 16) * (100 - percent) / 100) return f"#{max(0, min(255, r)):02x}{max(0, min(255, g)):02x}{max(0, min(255, b)):02x}" def select_workflow(self, item): """选择工作流程""" self.current_workflow = item.data(0, Qt.UserRole) self.current_stage_index = 0 self.update_workflow_display() self.update_task_display() self.update_artifacts_display() def select_stage(self, index): """选择阶段""" # 允许选择任何阶段,但会提示如果跳过未完成阶段 if index > self.current_stage_index: reply = QMessageBox.question( self, '确认跳转', '您正在跳转到未完成的阶段。确定要继续吗?', QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.No: return self.current_stage_index = index self.update_workflow_display() self.update_task_display() self.update_artifacts_display() def prev_stage(self): """返回上一阶段""" if self.current_stage_index > 0: self.current_stage_index -= 1 self.update_workflow_display() self.update_task_display() self.update_artifacts_display() self.status_bar.showMessage(f"已返回阶段: {self.current_workflow['stages'][self.current_stage_index]['name']}", 3000) def next_stage(self): """进入下一阶段""" if self.current_stage_index < len(self.current_workflow["stages"]) - 1: # 检查当前阶段是否已完成 current_stage = self.current_workflow["stages"][self.current_stage_index] # 检查成果物是否全部完成 if not current_stage.get("completed", False): QMessageBox.warning(self, "警告", "当前阶段成果物未全部完成,无法进入下一阶段!") return self.current_stage_index += 1 self.save_workflows() self.update_workflow_display() self.update_artifacts_display() self.status_bar.showMessage(f"已进入新阶段: {self.current_workflow['stages'][self.current_stage_index]['name']}", 3000) def check_artifacts_completed(self): """检查成果物是否全部完成""" for i in range(self.artifacts_list.count()): item = self.artifacts_list.item(i) widget = self.artifacts_list.itemWidget(item) checkbox = widget.findChild(QCheckBox) if not checkbox.isChecked(): return False return True def update_buttons_state(self): """更新按钮状态""" self.prev_stage_btn.setEnabled(self.current_stage_index > 0) self.next_stage_btn.setEnabled( self.current_stage_index < len(self.current_workflow["stages"]) - 1 ) def new_workflow(self): """新建工作流程""" dialog = WorkflowDialog(self) if dialog.exec_() == QDialog.Accepted: new_workflow = dialog.get_workflow() self.workflows.append(new_workflow) self.current_workflow = new_workflow self.current_stage_index = 0 self.save_workflows() self.update_workflow_display() self.update_task_display() self.update_artifacts_display() def edit_workflow(self): """编辑工作流程""" if not self.current_workflow: return dialog = WorkflowDialog(self, self.current_workflow) if dialog.exec_() == QDialog.Accepted: edited_workflow = dialog.get_workflow() index = self.workflows.index(self.current_workflow) self.workflows[index] = edited_workflow self.current_workflow = edited_workflow self.save_workflows() self.update_workflow_display() self.update_task_display() self.update_artifacts_display() def delete_workflow(self): """删除当前工作流程""" if not self.current_workflow: QMessageBox.warning(self, "警告", "请先选择一个工作流程!") return reply = QMessageBox.question( self, '确认删除', f"确定要删除工作流程【{self.current_workflow['name']}】吗?\n此操作不可恢复!", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: # 从工作流程列表中移除当前工作流程 self.workflows.remove(self.current_workflow) # 更新当前选中的工作流程 if self.workflows: self.current_workflow = self.workflows[0] self.current_stage_index = 0 else: self.current_workflow = None self.current_stage_index = -1 # 保存更改并更新界面 self.save_workflows() self.update_workflow_display() self.update_task_display() self.update_artifacts_display() self.status_bar.showMessage(f"工作流程已删除", 3000) def update_task_display(self): """更新成果物显示""" self.update_artifacts_display() def update_artifacts_display(self): """更新成果物显示""" self.artifacts_list.clear() if not self.current_workflow: return current_stage = self.current_workflow["stages"][self.current_stage_index] self.artifacts_label.setText(f"成果物检查 - {current_stage['name']}") for artifact in current_stage.get("artifacts", []): item = QListWidgetItem() widget = QWidget() layout = QHBoxLayout(widget) layout.setContentsMargins(10, 5, 10, 5) checkbox = QCheckBox(artifact) checkbox.setStyleSheet("font-size: 11px;") checkbox.stateChanged.connect(self.update_stage_status) layout.addWidget(checkbox) item.setSizeHint(widget.sizeHint()) self.artifacts_list.addItem(item) self.artifacts_list.setItemWidget(item, widget) # 更新状态信息 self.update_stage_status() def update_stage_status(self): """更新阶段状态""" if not self.current_workflow: return current_stage = self.current_workflow["stages"][self.current_stage_index] total_artifacts = len(current_stage.get("artifacts", [])) completed_artifacts = 0 for i in range(self.artifacts_list.count()): item = self.artifacts_list.item(i) widget = self.artifacts_list.itemWidget(item) checkbox = widget.findChild(QCheckBox) if checkbox.isChecked(): completed_artifacts += 1 # 更新状态文本 if total_artifacts == 0: status_text = "当前阶段没有成果物" status_color = "#6c757d" elif completed_artifacts == 0: status_text = "状态: 待开始 (0% 完成)" status_color = "#e74a3b" current_stage["completed"] = False elif completed_artifacts < total_artifacts: percent = (completed_artifacts / total_artifacts) * 100 status_text = f"状态: 进行中 ({percent:.0f}% 完成)" status_color = "#f6c23e" current_stage["completed"] = False else: status_text = "状态: 已完成 (100% 完成)" status_color = "#1cc88a" current_stage["completed"] = True self.status_info.setText(status_text) self.status_info.setStyleSheet(f""" font-size: 14px; font-weight: 500; color: white; background-color: {status_color}; padding: 10px; border-radius: 6px; margin-top: 10px; """) self.update_buttons_state() def show_stats(self): """显示统计信息""" if not self.current_workflow: return stats_text = f"<h2 style='color: #4e73df;'>{self.current_workflow['name']} 统计</h2>" # 按阶段统计 stats_text += "<h3 style='color: #5a5c69;'>按阶段统计</h3><ul style='list-style-type: none; padding-left: 0;'>" for i, stage in enumerate(self.current_workflow["stages"]): total_artifacts = len(stage.get("artifacts", [])) status = "已完成" if stage.get("completed", False) else "进行中" if i == self.current_stage_index else "未开始" stats_text += f"<li style='margin-bottom: 8px;'>" stats_text += f"<span style='display: inline-block; width: 20px; height: 20px; background-color: {stage['color']}; border-radius: 4px; margin-right: 10px;'></span>" stats_text += f"<b>{stage['name']}</b>: {total_artifacts} 个成果物 (<span style='color: #4e73df;'>{status}</span>)" stats_text += "</li>" stats_text += "</ul>" # 完成率统计 completed_stages = sum(1 for stage in self.current_workflow["stages"] if stage.get("completed", False)) total_stages = len(self.current_workflow["stages"]) completion_rate = (completed_stages / total_stages) * 100 if total_stages > 0 else 0 # 进度条 stats_text += f"<h3 style='color: #5a5c69;'>流程完成率: {completion_rate:.0f}%</h3>" stats_text += f""" <div style='background-color: #e9ecef; height: 20px; border-radius: 10px; margin-bottom: 20px;'> <div style='background-color: #4e73df; width: {completion_rate}%; height: 100%; border-radius: 10px;'></div> </div> """ self.stats_label.setText(stats_text) self.artifacts_management.setCurrentWidget(self.stats_widget) class WorkflowDialog(QDialog): """工作流程编辑对话框""" def __init__(self, parent=None, workflow=None): super().__init__(parent) self.setWindowTitle("编辑工作流程" if workflow else "新建工作流程") self.resize(700, 500) self.workflow = workflow if workflow else { "name": "", "description": "", "stages": [], "tasks": [] } self.setup_ui() def setup_ui(self): """设置对话框UI""" layout = QVBoxLayout(self) layout.setContentsMargins(20, 20, 20, 20) # 标题 title = QLabel("编辑工作流程" if self.workflow.get("name") else "新建工作流程") title.setStyleSheet("font-size: 18px; font-weight: 600; color: #4e73df; margin-bottom: 20px;") layout.addWidget(title) # 基本信息 form_layout = QFormLayout() form_layout.setLabelAlignment(Qt.AlignRight) form_layout.setVerticalSpacing(15) self.name_edit = QLineEdit(self.workflow["name"]) self.name_edit.setPlaceholderText("输入工作流程名称") form_layout.addRow("流程名称:", self.name_edit) self.desc_edit = QTextEdit(self.workflow["description"]) self.desc_edit.setPlaceholderText("输入工作流程描述") self.desc_edit.setFixedHeight(80) form_layout.addRow("流程描述:", self.desc_edit) layout.addLayout(form_layout) # 阶段管理 stages_label = QLabel("阶段管理") stages_label.setStyleSheet("font-size: 16px; font-weight: 600; color: #4e73df; margin-top: 20px; margin-bottom: 10px;") layout.addWidget(stages_label) # 阶段列表和编辑区 stages_container = QHBoxLayout() stages_container.setSpacing(20) # 阶段列表 stages_list_container = QVBoxLayout() self.stages_list = QListWidget() self.stages_list.setFixedHeight(180) self.update_stages_list() self.stages_list.itemClicked.connect(self.select_stage) stages_list_container.addWidget(self.stages_list) stages_container.addLayout(stages_list_container, 1) # 阶段编辑区 stage_edit_container = QVBoxLayout() self.stage_edit = QWidget() self.stage_edit.setStyleSheet("background-color: #f8f9fa; border-radius: 8px; padding: 15px;") stage_edit_layout = QFormLayout(self.stage_edit) stage_edit_layout.setVerticalSpacing(12) self.stage_name_edit = QLineEdit() self.stage_name_edit.setPlaceholderText("输入阶段名称") stage_edit_layout.addRow("阶段名称:", self.stage_name_edit) self.stage_desc_edit = QTextEdit() self.stage_desc_edit.setPlaceholderText("输入阶段描述") self.stage_desc_edit.setFixedHeight(60) stage_edit_layout.addRow("阶段描述:", self.stage_desc_edit) self.stage_color_edit = QComboBox() self.stage_color_edit.addItems(["#4e73df", "#1cc88a", "#36b9cc", "#f6c23e", "#e74a3b", "#6f42c1", "#fd7e14", "#20c997"]) stage_edit_layout.addRow("阶段颜色:", self.stage_color_edit) # 成果物编辑 self.artifacts_edit = QTextEdit() self.artifacts_edit.setPlaceholderText("每行输入一个成果物名称") stage_edit_layout.addRow("成果物:", self.artifacts_edit) stage_edit_container.addWidget(self.stage_edit) # 阶段操作按钮 stage_btn_layout = QHBoxLayout() stage_btn_layout.setSpacing(10) self.add_stage_btn = QPushButton("添加阶段") self.add_stage_btn.setStyleSheet("background-color: #1cc88a; color: white;") self.add_stage_btn.clicked.connect(self.add_stage) stage_btn_layout.addWidget(self.add_stage_btn) self.update_stage_btn = QPushButton("更新阶段") self.update_stage_btn.setStyleSheet("background-color: #4e73df; color: white;") self.update_stage_btn.clicked.connect(self.update_stage) stage_btn_layout.addWidget(self.update_stage_btn) self.remove_stage_btn = QPushButton("删除阶段") self.remove_stage_btn.setStyleSheet("background-color: #e74a3b; color: white;") self.remove_stage_btn.clicked.connect(self.remove_stage) stage_btn_layout.addWidget(self.remove_stage_btn) stage_edit_container.addLayout(stage_btn_layout) stages_container.addLayout(stage_edit_container, 2) layout.addLayout(stages_container) # 对话框按钮 btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignRight) btn_layout.setSpacing(15) save_btn = QPushButton("保存") save_btn.setStyleSheet("background-color: #4e73df; color: white; min-width: 100px;") save_btn.clicked.connect(self.accept) btn_layout.addWidget(save_btn) cancel_btn = QPushButton("取消") cancel_btn.setStyleSheet("background-color: #6c757d; color: white; min-width: 100px;") cancel_btn.clicked.connect(self.reject) btn_layout.addWidget(cancel_btn) layout.addLayout(btn_layout) def update_stages_list(self): """更新阶段列表""" self.stages_list.clear() for stage in self.workflow["stages"]: item = QListWidgetItem(stage["name"]) item.setData(Qt.UserRole, stage) self.stages_list.addItem(item) def select_stage(self, item): """选择阶段进行编辑""" stage = item.data(Qt.UserRole) self.stage_name_edit.setText(stage["name"]) self.stage_desc_edit.setText(stage["description"]) self.stage_color_edit.setCurrentText(stage["color"]) self.artifacts_edit.setText("\n".join(stage.get("artifacts", []))) def add_stage(self): """添加新阶段""" name = self.stage_name_edit.text().strip() if not name: QMessageBox.warning(self, "警告", "请输入阶段名称!") return new_stage = { "name": name, "description": self.stage_desc_edit.toPlainText().strip(), "color": self.stage_color_edit.currentText(), "artifacts": [a.strip() for a in self.artifacts_edit.toPlainText().split("\n") if a.strip()], "completed": False } self.workflow["stages"].append(new_stage) self.update_stages_list() self.clear_stage_edit() def update_stage(self): """更新当前阶段""" current_item = self.stages_list.currentItem() if not current_item: QMessageBox.warning(self, "警告", "请先选择一个阶段!") return name = self.stage_name_edit.text().strip() if not name: QMessageBox.warning(self, "警告", "请输入阶段名称!") return stage = current_item.data(Qt.UserRole) stage["name"] = name stage["description"] = self.stage_desc_edit.toPlainText().strip() stage["color"] = self.stage_color_edit.currentText() stage["artifacts"] = [a.strip() for a in self.artifacts_edit.toPlainText().split("\n") if a.strip()] self.update_stages_list() def remove_stage(self): """删除当前阶段""" current_item = self.stages_list.currentItem() if not current_item: QMessageBox.warning(self, "警告", "请先选择一个阶段!") return reply = QMessageBox.question(self, '确认删除', f"确定要删除阶段【{current_item.text()}】吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: stage = current_item.data(Qt.UserRole) self.workflow["stages"].remove(stage) self.update_stages_list() self.clear_stage_edit() def clear_stage_edit(self): """清空阶段编辑区""" self.stage_name_edit.clear() self.stage_desc_edit.clear() self.stage_color_edit.setCurrentIndex(0) self.artifacts_edit.clear() def get_workflow(self): """获取编辑后的工作流程""" return { "name": self.name_edit.text().strip(), "description": self.desc_edit.toPlainText().strip(), "stages": self.workflow["stages"], "tasks": self.workflow["tasks"] } if __name__ == "__main__": app = QApplication(sys.argv) # 设置应用程序样式 app.setStyle("Fusion") palette = QPalette() palette.setColor(QPalette.Window, QColor(248, 249, 250)) palette.setColor(QPalette.WindowText, QColor(73, 80, 87)) palette.setColor(QPalette.Base, QColor(255, 255, 255)) palette.setColor(QPalette.AlternateBase, QColor(248, 249, 250)) palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 255)) palette.setColor(QPalette.ToolTipText, QColor(73, 80, 87)) palette.setColor(QPalette.Text, QColor(73, 80, 87)) palette.setColor(QPalette.Button, QColor(248, 249, 250)) palette.setColor(QPalette.ButtonText, QColor(73, 80, 87)) palette.setColor(QPalette.BrightText, QColor(255, 255, 255)) palette.setColor(QPalette.Highlight, QColor(78, 115, 223)) palette.setColor(QPalette.HighlightedText, QColor(255, 255, 255)) app.setPalette(palette) # 设置应用字体 app_font = QFont("Segoe UI", 10) app.setFont(app_font) manager = WorkflowManager() manager.show() sys.exit(app.exec_()) 这个代码有些问题,我选中已完成的成果物,选中其他阶段,再次选中时,会变成未开始状态,无法保持,请修正,另外请优化布局,现在的界面太大了
07-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值