C++ web开发框架 paozhu 发布1.15.0 继续添加企业开发周边

目前使用 paozhu全家桶开发企业管理后台,遇到的需要后台功能也顺手添加了
paozhu 对AI还是非常友好,基本上可以让AI在paozhu框架基础上生成立即,马上可以使用后台。

你一行代码都不用写,先把系统设计文档、数据库设计好,然后放到项目docx目录,然后AI读取并开工。

1 本次主要更新了ORM文件和view视图问题更新方式
./bin/paozhu_cli view 是更新所有需要更新的视图文件
./bin/paozhu_cli orm <dbtag> 更新数据库表结构到CPP文件,<dbtag> 是conf/orm.conf 里面 [dbtag] 的数据库标签

./bin/paozhu_cli json 是框架搜索libs目录下 struct 结构体生成 json_encode json_decode,或者根据json文件生成相应的
c++结构体,然后再生成json_encode json_decode 让json和结构体互相转化

2 添加webpay,支持微信和支付宝,阿里云和腾讯云短信接口

#include <chrono>
#include <thread>
#include <filesystem>
#include "httppeer.h"
#include "test_wxpay.h"
#include "func.h"
#include "request.h"
#include "httpclient.h"
#ifdef ENABLE_WEBPAY
#include "wxpay.h"
#endif
#ifdef ENABLE_IMAGE
#include "qrcode.h"
#include "pzpng.h"
#endif

namespace http
{

//@urlpath(null,testwxpaynative)
std::string test_wxpay_native(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = get_date("%Y%m%d%H%M%S") + rand_string(4, 4);

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");
    wp.setOutTradeNo(out_trade_no);
    wp.setDescription("测试商品");
    wp.setTotalAmount("1");
    wp.setNotifyUrl("https://yourdomain.com/wxpaynotify");

    std::string response = wp.createNative();
    client.output = response;

#ifdef ENABLE_IMAGE
    http::obj_val resp_json;
    resp_json.from_json(response);
    std::string qrcontent = resp_json["code_url"].to_string();

    if (!qrcontent.empty()) {
        namespace fs = std::filesystem;
        qr::qrcode q;
        q.text(qrcontent, qr::Ecc::M, 1);
        image::png img;
        unsigned char scale = 10;
        img.create(q.width()*scale + 40, q.height()*scale + 40, 6, 8);
        img.fillColor({255, 255, 255, 255});
        img.qrdata(q.data,q.width(),q.height(),scale, 20 ,20);
        
        std::string wwwpath;
        wwwpath.append(client.get_sitepath());
        wwwpath.append("/upload");

        fs::path paths = wwwpath;
        bool is_success = fs::create_directories(paths);
        if (is_success) {
            fs::permissions(paths,
                            fs::perms::owner_all | fs::perms::group_all | fs::perms::others_read,
                            fs::perm_options::add);
        }

        wwwpath.append("/wxpay_qr.png");
        img.save(wwwpath);

        client << "<img src=\"/upload/wxpay_qr.png?token=" << rand_string(6, 6) << "\">";
    }
#endif
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,testwxpayjsapi)
std::string test_wxpay_jsapi(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = get_date("%Y%m%d%H%M%S") + rand_string(4, 4);

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");
    wp.setOutTradeNo(out_trade_no);
    wp.setDescription("测试商品");
    wp.setTotalAmount("1");
    wp.setNotifyUrl("https://yourdomain.com/wxpaynotify");
    wp.setOpenId("用户的OpenID");

    std::string response = wp.createJSAPI();
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,testwxpayapp)
std::string test_wxpay_app(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = get_date("%Y%m%d%H%M%S") + rand_string(4, 4);

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");
    wp.setOutTradeNo(out_trade_no);
    wp.setDescription("测试商品");
    wp.setTotalAmount("1");
    wp.setNotifyUrl("https://yourdomain.com/wxpaynotify");

    std::string response = wp.createAPP();
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,testwxpayh5)
std::string test_wxpay_h5(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = get_date("%Y%m%d%H%M%S") + rand_string(4, 4);

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");
    wp.setOutTradeNo(out_trade_no);
    wp.setDescription("测试商品");
    wp.setTotalAmount("1");
    wp.setNotifyUrl("https://yourdomain.com/wxpaynotify");

    std::string response = wp.createH5();
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,testwxpayquery)
std::string test_wxpay_query(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = client.get["out_trade_no"].to_string();

    if (out_trade_no.empty()) {
        client.output = "{\"code\":\"-1\",\"msg\":\"缺少out_trade_no\"}";
        return "";
    }

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");

    std::string response = wp.queryTrade(out_trade_no);
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,testwxpayclose)
std::string test_wxpay_close(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = client.get["out_trade_no"].to_string();

    if (out_trade_no.empty()) {
        client.output = "{\"code\":\"-1\",\"msg\":\"缺少out_trade_no\"}";
        return "";
    }

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");

    std::string response = wp.closeTrade(out_trade_no);
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,testwxpayrefund)
std::string test_wxpay_refund(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    std::string out_trade_no = client.get["out_trade_no"].to_string();
    std::string refund_amount = client.get["refund_amount"].to_string();

    if (out_trade_no.empty()) {
        client.output = "{\"code\":\"-1\",\"msg\":\"缺少out_trade_no\"}";
        return "";
    }
    if (refund_amount.empty()) {
        client.output = "{\"code\":\"-1\",\"msg\":\"缺少refund_amount\"}";
        return "";
    }

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");
    wp.setSerialNo("你的证书序列号");
    wp.setTotalAmount("1");

    std::string response = wp.refundTrade(refund_amount, "测试退款", out_trade_no);
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,wxpaynotify)
std::string test_wxpay_notify(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
    client.type("Content-Type: application/json; charset=utf-8");
#ifdef ENABLE_WEBPAY
    std::map<std::string, std::string> headers;
    headers["Wechatpay-Timestamp"] = client.get_header("Wechatpay-Timestamp");
    headers["Wechatpay-Nonce"] = client.get_header("Wechatpay-Nonce");
    headers["Wechatpay-Signature"] = client.get_header("Wechatpay-Signature");
    headers["Wechatpay-Serial"] = client.get_header("Wechatpay-Serial");

    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setPublicKey("你的微信平台证书路径或内容");
    wp.setApiKey("你的微信API密钥");

    auto resp = wp.handleNotify(client.rawcontent, headers);

    if (resp["status_code"] == "0") {
        std::string out_trade_no = resp["out_trade_no"];
        std::string transaction_id = resp["transaction_id"];
        std::string amount = resp["amount"];
        client.output = "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
    } else {
        client.output = "{\"code\":\"FAIL\",\"message\":\"" + resp["error_msg"] + "\"}";
    }
#else
    client.output = "{\"code\":\"FAIL\",\"message\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

//@urlpath(null,wxpaydownloadcert)
std::string test_wxpay_download_cert(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_WEBPAY
    pay::wxpay wp;
    wp.setAppId("你的微信APPID");
    wp.setMchId("你的微信商户号");
    wp.setPrivateKey("你的微信商户私钥路径或内容");
    wp.setApiKey("你的微信API密钥");

    std::string response = wp.downloadCertificates();
    client.output = response;
#else
    client.output = "{\"code\":\"-1\",\"msg\":\"ENABLE_WEBPAY not enabled\"}";
#endif

    return "";
}

}

3 添加图表生成,满足企业后台开发需要,支持20种图表,算法来自echarts, 目前使用纯c++生成svg图片,添加了正态分布 九宫格 乔哈里视窗图等

//@urlpath(null,test_svgstats)
std::string test_svgstats(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_CHART
    client << "<h2>========== 原始数据 & 统计结果 ==========</h2>";

    // ============================================================
    // 一、LinearRegression 示例(原始数据)
    // ============================================================
    client << "<h3>LinearRegression 示例</h3>";

    // ---- 示例 1: 线性回归 y = ax + b ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        // 面积(m²) → 价格(万元)
        std::vector<std::vector<double>> data = {
            {50, 150},
            {70, 210},
            {90, 270},
            {110, 330},
            {130, 390}};
        auto result = LinearRegression::linear(data);
        oss << "<b>【线性回归】房价 = a * 面积 + b</b><br>";
        oss << "  公式:    " << result.expression << "<br>";
        oss << "  斜率 a:  " << result.parameters.gradient << "<br>";
        oss << "  截距 b:  " << result.parameters.intercept << "<br>";
        oss << "  预测点:  ";
        for (const auto &p : result.points)
        {
            oss << "(" << p[0] << "," << p[1] << ") ";
        }
        oss << "<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 2: 指数回归 y = a * e^(bx) ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {1, 4862},
            {2, 5294},
            {3, 5934},
            {4, 7171},
            {5, 8964},
            {6, 10202},
            {7, 11962},
            {8, 14928},
            {9, 16909},
            {10, 18547},
            {11, 21617},
            {12, 26638},
            {13, 34634},
            {14, 46759},
            {15, 58478},
            {16, 67884},
            {17, 74462},
            {18, 79395}};
        auto result = LinearRegression::exponential(data);
        oss << "<b>【指数回归】GDP = a * e^(b*年份)</b><br>";
        oss << "  公式:    " << result.expression << "<br>";
        oss << "  系数 a:  " << result.parameters.intercept << "<br>";
        oss << "  指数 b:  " << result.parameters.gradient << "<br>";
        oss << "  预测第 20 年: " << result.parameters.intercept
            << " * e^(" << result.parameters.gradient << " * 20) = "
            << result.parameters.intercept * std::exp(result.parameters.gradient * 20)
            << "<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 3: 对数回归 y = a + b*ln(x) ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {1, 5},
            {2, 12},
            {3, 17},
            {5, 22},
            {10, 29},
            {20, 35},
            {50, 42}};
        auto result = LinearRegression::logarithmic(data);
        oss << "<b>【对数回归】收益 = a + b*ln(投入)</b><br>";
        oss << "  公式:    " << result.expression << "<br>";
        oss << "  截距 a:  " << result.parameters.intercept << "<br>";
        oss << "  斜率 b:  " << result.parameters.gradient << "<br>";
        oss << "  投入 100 时预测收益: " << result.parameters.intercept
            << " + " << result.parameters.gradient << " * ln(100) = "
            << result.parameters.intercept + result.parameters.gradient * std::log(100)
            << "<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 4: 多项式回归 y = a0 + a1*x + a2*x^2 + ... ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {0, 0},
            {1, 1.2},
            {2, 3.8},
            {3, 8.9},
            {4, 16.1},
            {5, 25.0}};
        auto result = LinearRegression::polynomial(data, 2);
        oss << "<b>【多项式回归 (order=2)】抛物线拟合</b><br>";
        oss << "  公式:    " << result.expression << "<br>";
        oss << "  系数:    ";
        for (size_t i = 0; i < result.parameters.coefficients.size(); ++i)
        {
            oss << "a" << i << "=" << result.parameters.coefficients[i] << " ";
        }
        oss << "<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 5: 过原点线性回归 y = ax ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {1, 3.1},
            {2, 5.9},
            {3, 9.2},
            {4, 11.8},
            {5, 15.2}};
        LinearRegression::Config config;
        config.method = LinearRegression::LINEAR_THROUGH_ORIGIN;
        auto result   = LinearRegression::run(data, config);
        oss << "<b>【过原点线性回归】距离 = a * 时间</b><br>";
        oss << "  公式:    " << result.expression << "<br>";
        oss << "  速度 a:  " << result.parameters.gradient << " m/s<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 6: 多维指定列 ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {1, 0, 3},
            {2, 0, 5},
            {3, 0, 7},
            {4, 0, 9},
            {5, 0, 11}};
        LinearRegression::Config config;
        config.method     = LinearRegression::LINEAR;
        config.dimensions = {0, 2};// x=第0列, y=第2列
        auto result       = LinearRegression::run(data, config);
        oss << "<b>【指定维度】x=col0, y=col2</b><br>";
        oss << "  公式:    " << result.expression << "<br>";
        oss << "  预测点:  ";
        for (const auto &p : result.points)
        {
            oss << "(" << p[0] << "," << p[1] << ") ";
        }
        oss << "<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ============================================================
    // 二、ClusteringProcess 示例(原始数据)
    // ============================================================
    client << "<h3>ClusteringProcess 示例</h3>";

    // ---- 示例 1: 客户分群 ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(1);
        std::vector<std::vector<double>> data = {
            {2, 1},
            {3, 2},
            {2.5, 1},// 低价值客户
            {15, 8},
            {18, 10},
            {16, 9},// 中等客户
            {50, 25},
            {55, 30},
            {48, 28}// 高价值客户
        };
        auto result = ClusteringProcess::simpleKMeans(data, 3);
        oss << "<b>【客户分群】k=3</b><br>";
        oss << "  原始数据: {消费额(千元), 月访问次数}<br>";
        for (size_t i = 0; i < data.size(); ++i)
        {
            oss << "    点" << i << ": (" << data[i][0] << ", " << data[i][1] << ")<br>";
        }
        oss << "  聚类结果:<br>";
        for (size_t c = 0; c < result.centroids.size(); ++c)
        {
            oss << "    群 " << c << " 中心: (消费="
                << result.centroids[c][0] << "k, 访问="
                << result.centroids[c][1] << "次)  "
                << result.pointsInCluster[c].size() << " 人<br>";
        }
        oss << "<br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 2: 二维散点聚类 + 每个点的分配 ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {0.1, 0.2},
            {0.2, 0.1},
            {0.3, 0.3},
            {0.1, 0.4},
            {0.5, 0.2},
            {9.8, 9.9},
            {9.9, 9.8},
            {10.1, 10.0},
            {10.0, 10.2},
            {9.7, 10.1}};
        auto result = ClusteringProcess::simpleKMeans(data, 2);
        oss << "<b>【二维散点聚类】k=2,10 个点</b><br>";
        oss << "  各点分配: ";
        for (size_t i = 0; i < result.clusterAssigned.size(); ++i)
        {
            oss << "点" << i << "→群" << result.clusterAssigned[i].clusterIndex << " ";
        }
        oss << "<br>";
        oss << "  群 0 中心: (" << result.centroids[0][0] << ", " << result.centroids[0][1] << ")<br>";
        oss << "  群 1 中心: (" << result.centroids[1][0] << ", " << result.centroids[1][1] << ")<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 3: 多维数据聚类 + 指定维度 ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {1.0, 2.0, 100},
            {1.1, 2.1, 101},
            {10.0, 20.0, 200},
            {10.1, 20.1, 201}};
        ClusteringProcess::Config config;
        config.clusterCount = 2;
        config.dimensions   = {0, 1};// 只用第 0 和第 1 列做聚类
        auto result         = ClusteringProcess::run(data, config);
        oss << "<b>【多维聚类】3D 数据,用前 2 维聚类</b><br>";
        oss << "  群 0 中心: (" << result.centroids[0][0] << ", " << result.centroids[0][1] << ")<br>";
        oss << "  群 1 中心: (" << result.centroids[1][0] << ", " << result.centroids[1][1] << ")<br>";
        oss << "  点 0,1 → 群 " << result.clusterAssigned[0].clusterIndex << "<br>";
        oss << "  点 2,3 → 群 " << result.clusterAssigned[2].clusterIndex << "<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ---- 示例 4: 每个点的距离信息 ----
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<std::vector<double>> data = {
            {0.1, 0.2},
            {1.0, 1.0},
            {0.3, 0.1},
            {9.8, 9.9},
            {9.9, 9.8},
            {10.0, 10.0}};
        auto result = ClusteringProcess::simpleKMeans(data, 2);
        oss << "<b>【距离信息】每个点到其所在群中心的距离</b><br>";
        for (size_t i = 0; i < result.clusterAssigned.size(); ++i)
        {
            oss << "  点" << i << " (" << data[i][0] << "," << data[i][1]
                << ") → 群" << result.clusterAssigned[i].clusterIndex
                << "  距离²=" << result.clusterAssigned[i].distance << "<br>";
        }
        oss << "<br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ============================================================
    // 三、KernelDensity 测试(原始数据)
    // ============================================================
    client << "<h3>KernelDensity 测试</h3>";
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);
        std::vector<double> data = {1.0, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0};
        KernelDensity kde(data, 0);
        oss << "  原始数据: ";
        for (size_t i = 0; i < data.size(); ++i)
        {
            oss << data[i] << " ";
        }
        oss << "<br>";
        oss << "  density(2.5) = " << kde.density(2.5) << "<br>";
        double q50 = kde.quantile(0.5);
        oss << "  quantile(0.5) = " << q50 << " (data range: " << kde.dataMin() << " ~ " << kde.dataMax() << ")<br>";
        oss << "  generateCurve(10).size() = " << kde.generateCurve(10).size() << "<br>";
        auto ci = kde.centralInterval(0.5);
        oss << "  centralInterval(0.5) = (" << ci.first << ", " << ci.second << ")<br><br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ============================================================
    // 四、LinearRegression 单元测试
    // ============================================================
    client << "<h3>LinearRegression 单元测试</h3>";
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);

        // Test 1: Simple linear regression y = 2x + 1
        {
            std::vector<std::vector<double>> data = {
                {1, 3},
                {2, 5},
                {3, 7},
                {4, 9},
                {5, 11}};
            auto result = LinearRegression::linear(data);
            bool pass   = std::abs(result.parameters.gradient - 2.0) < 0.01 && std::abs(result.parameters.intercept - 1.0) < 0.01;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": linear y = 2x + 1, "
                << "a=" << result.parameters.gradient << " b=" << result.parameters.intercept << "<br>";
        }

        // Test 2: Linear through origin y = 3x
        {
            std::vector<std::vector<double>> data = {
                {1, 3},
                {2, 6},
                {3, 9},
                {4, 12},
                {5, 15}};
            LinearRegression::Config config;
            config.method = LinearRegression::LINEAR_THROUGH_ORIGIN;
            auto result   = LinearRegression::run(data, config);
            bool pass     = std::abs(result.parameters.gradient - 3.0) < 0.01;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": linear through origin y = 3x, "
                << "a=" << result.parameters.gradient << "<br>";
        }

        // Test 3: Exponential regression y = 2 * e^(0.5*x)
        {
            std::vector<std::vector<double>> data;
            for (int i = 1; i <= 10; ++i)
            {
                data.push_back({static_cast<double>(i), 2.0 * std::exp(0.5 * i)});
            }
            auto result = LinearRegression::exponential(data);
            bool pass   = std::abs(result.parameters.gradient - 0.5) < 0.05 && std::abs(result.parameters.intercept - 2.0) < 0.5;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": exponential y = 2e^(0.5x), "
                << "a=" << result.parameters.intercept << " b=" << result.parameters.gradient << "<br>";
        }

        // Test 4: Logarithmic regression y = 1 + 2*ln(x)
        {
            std::vector<std::vector<double>> data;
            for (int i = 1; i <= 10; ++i)
            {
                data.push_back({static_cast<double>(i), 1.0 + 2.0 * std::log(i)});
            }
            auto result = LinearRegression::logarithmic(data);
            bool pass   = std::abs(result.parameters.intercept - 1.0) < 0.01 && std::abs(result.parameters.gradient - 2.0) < 0.01;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": logarithmic y = 1 + 2ln(x), "
                << "a=" << result.parameters.intercept << " b=" << result.parameters.gradient << "<br>";
        }

        // Test 5: Polynomial regression y = x^2
        {
            std::vector<std::vector<double>> data;
            for (int i = 0; i <= 5; ++i)
            {
                data.push_back({static_cast<double>(i), static_cast<double>(i * i)});
            }
            auto result = LinearRegression::polynomial(data, 2);
            bool pass   = result.parameters.coefficients.size() == 3 && std::abs(result.parameters.coefficients[2] - 1.0) < 0.01;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": polynomial y = x^2, "
                << "a2=" << result.parameters.coefficients[2] << "<br>";
        }

        // Test 6: Real-world data (ECharts example)
        {
            std::vector<std::vector<double>> data = {
                {1, 4862.4},
                {2, 5294.7},
                {3, 5934.5},
                {4, 7171.0},
                {5, 8964.4},
                {6, 10202.2},
                {7, 11962.5},
                {8, 14928.3},
                {9, 16909.2},
                {10, 18547.9},
                {11, 21617.8},
                {12, 26638.1},
                {13, 34634.4},
                {14, 46759.4},
                {15, 58478.1},
                {16, 67884.6},
                {17, 74462.6},
                {18, 79395.7}};
            auto result = LinearRegression::exponential(data);
            bool pass   = result.points.size() == 18;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": real-world exponential (18 points), "
                << "expression=" << result.expression << "<br>";
        }
        oss << "<br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ============================================================
    // 五、Clustering 单元测试
    // ============================================================
    client << "<h3>Clustering 单元测试</h3>";
    {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(4);

        // Test 1: Two clearly separated clusters
        {
            std::vector<std::vector<double>> data = {
                {0.1, 0.2},
                {0.2, 0.1},
                {0.3, 0.3},
                {0.1, 0.4},
                {0.5, 0.2},
                {9.8, 9.9},
                {9.9, 9.8},
                {10.1, 10.0},
                {10.0, 10.2},
                {9.7, 10.1}};
            auto result = ClusteringProcess::simpleKMeans(data, 2);
            int c0      = result.clusterAssigned[0].clusterIndex;
            int c1      = result.clusterAssigned[5].clusterIndex;
            bool pass   = result.centroids.size() == 2 && c0 != c1;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": 2-cluster separation (c0=" << c0 << " c1=" << c1 << ")<br>";
        }

        // Test 2: Three clusters
        {
            std::vector<std::vector<double>> data = {
                {0.1, 0.2},
                {0.2, 0.3},
                {0.3, 0.1},
                {5.0, 5.1},
                {5.1, 5.0},
                {4.9, 5.2},
                {9.8, 9.9},
                {9.9, 9.7},
                {10.0, 10.1}};
            auto result      = ClusteringProcess::simpleKMeans(data, 3);
            bool allNonEmpty = true;
            for (size_t c = 0; c < result.pointsInCluster.size(); ++c)
            {
                if (result.pointsInCluster[c].size() < 1)
                    allNonEmpty = false;
            }
            bool pass = result.centroids.size() == 3 && allNonEmpty;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": 3-cluster, all non-empty<br>";
        }

        // Test 3: Multi-dimensional data
        {
            std::vector<std::vector<double>> data = {
                {1.0, 2.0, 3.0},
                {1.1, 2.1, 3.1},
                {10.0, 20.0, 30.0},
                {10.1, 20.1, 30.1}};
            auto result = ClusteringProcess::simpleKMeans(data, 2);
            bool pass   = result.centroids.size() == 2 && result.clusterAssigned[0].clusterIndex != result.clusterAssigned[2].clusterIndex;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": 3D clustering<br>";
        }

        // Test 4: Edge case - tight data
        {
            std::vector<std::vector<double>> data = {
                {1.0, 2.0},
                {1.1, 2.1},
                {0.9, 1.9}};
            auto result = ClusteringProcess::simpleKMeans(data, 2);
            bool pass   = result.centroids.size() >= 1;
            oss << "  " << (pass ? "PASS" : "FAIL") << ": tight data edge case<br>";
        }
        oss << "<br>";
        client << "<pre>" << oss.str() << "</pre>";
    }

    // ============================================================
    // 六、SVG 图表输出
    // ============================================================
    client << "<hr><h2>========== SVG 图表 ==========</h2>";

    client << "<p>21. 线性回归图 - 房价预测</p>";
    {
        SvgRegressionChart chart(800, 500);
        chart.setTitle("Linear Regression - House Price Prediction");
        chart.setData({{50, 150}, {60, 175}, {70, 210}, {80, 240}, {90, 270}, {100, 300}, {110, 330}, {120, 360}, {130, 390}, {140, 420}});
        chart.setMethod(LinearRegression::LINEAR);
        chart.setPointColor(SvgColor(84, 112, 198));
        chart.setLineColor(SvgColor(238, 102, 102));
        client << chart.render();
    }

    client << "<p>22. 指数回归图 - GDP 增长</p>";
    {
        SvgRegressionChart chart(800, 500);
        chart.setTitle("Exponential Regression - GDP Growth");
        chart.setData({{1, 2.1}, {2, 2.5}, {3, 3.1}, {4, 4.0}, {5, 5.2}, {6, 6.8}, {7, 8.9}, {8, 11.5}, {9, 14.8}, {10, 19.0}});
        chart.setMethod(LinearRegression::EXPONENTIAL);
        chart.setPointColor(SvgColor(60, 162, 131));
        chart.setLineColor(SvgColor(238, 102, 102));
        client << chart.render();
    }

    client << "<p>23. 对数回归图 - 学习曲线</p>";
    {
        SvgRegressionChart chart(800, 500);
        chart.setTitle("Logarithmic Regression - Learning Curve");
        chart.setData({{1, 20}, {2, 35}, {3, 45}, {4, 52}, {5, 57}, {6, 61}, {8, 67}, {10, 71}, {15, 78}, {20, 83}});
        chart.setMethod(LinearRegression::LOGARITHMIC);
        chart.setPointColor(SvgColor(250, 200, 88));
        chart.setLineColor(SvgColor(238, 102, 102));
        client << chart.render();
    }

    client << "<p>24. 多项式回归图 - 二次曲线</p>";
    {
        SvgRegressionChart chart(800, 500);
        chart.setTitle("Polynomial Regression - Quadratic");
        chart.setData({{0, 2.1}, {1, 7.7}, {2, 13.6}, {3, 27.2}, {4, 40.9}, {5, 61.1}, {6, 82.3}, {7, 108.5}, {8, 138.2}, {9, 170.0}});
        chart.setMethod(LinearRegression::POLYNOMIAL);
        chart.setPolynomialOrder(2);
        chart.setPointColor(SvgColor(154, 96, 180));
        chart.setLineColor(SvgColor(238, 102, 102));
        client << chart.render();
    }

    client << "<p>25. 聚类散点图 - 客户分群 (3 类)</p>";
    {
        SvgClusteringChart chart(800, 500);
        chart.setTitle("K-Means Clustering - Customer Segmentation");
        chart.setData({{2, 1}, {3, 2}, {2.5, 1.5}, {4, 3}, {3.5, 2.5}, {1.5, 1}, {2, 2.5}, {3, 1.5}, {12, 8}, {15, 10}, {14, 7}, {13, 9}, {16, 11}, {11, 8}, {15, 8.5}, {14, 10}, {45, 25}, {50, 30}, {48, 28}, {52, 32}, {55, 30}, {47, 26}, {50, 27}, {53, 31}});
        chart.setClusterCount(3);
        chart.setPointSize(7);
        chart.setCentroidSize(12);
        client << chart.render();
    }

    client << "<p>26. 聚类散点图 - 4 类</p>";
    {
        SvgClusteringChart chart(800, 500);
        chart.setTitle("K-Means Clustering - 4 Clusters");
        chart.setData({{5, 5}, {6, 4}, {4, 6}, {5, 7}, {6, 5}, {7, 4}, {4, 5}, {5, 25}, {6, 26}, {4, 24}, {5, 27}, {7, 25}, {6, 24}, {4, 26}, {25, 5}, {26, 4}, {24, 6}, {25, 7}, {27, 5}, {26, 6}, {24, 5}, {25, 25}, {26, 26}, {24, 24}, {25, 27}, {27, 25}, {26, 24}, {24, 26}});
        chart.setClusterCount(4);
        chart.setPointSize(7);
        chart.setCentroidSize(12);
        client << chart.render();
    }

    client << "<p>27. 多元线性回归 - 房价预测(面积+卧室数+房龄)</p>";
    {
        SvgRegressionChart chart(800, 500);
        chart.setTitle("Multivariate Linear Regression - House Price");
        chart.setData({{60, 2, 20, 180},
                       {80, 2, 15, 230},
                       {100, 3, 10, 310},
                       {120, 3, 8, 360},
                       {140, 3, 5, 420},
                       {160, 4, 15, 440},
                       {180, 4, 12, 500},
                       {200, 4, 10, 560},
                       {90, 2, 25, 200},
                       {110, 3, 18, 290},
                       {130, 3, 20, 340},
                       {150, 4, 8, 430},
                       {170, 4, 6, 490},
                       {190, 4, 5, 530},
                       {70, 2, 30, 160},
                       {85, 3, 22, 250},
                       {105, 3, 12, 320},
                       {125, 3, 7, 380},
                       {145, 4, 10, 410},
                       {165, 4, 8, 470}});
        chart.setMethod(LinearRegression::MULTIVARIATE_LINEAR);
        chart.setPointColor(SvgColor(84, 112, 198));
        chart.setLineColor(SvgColor(238, 102, 102));
        chart.setPointSize(6);
        client << chart.render();
    }

    client << "<p>All SVG stats tests completed!</p>";
#else
    client << "<p>Please: cmake .. -DENABLE_CHART=ON </p>";
#endif// ENABLE_CHART
    return "";
}

4 添加pzword html转word(docx格式) word(docx格式)到html, 支持文字颜色 大小 表格和图片简单核心功能读写

#include "httppeer.h"
#include "serverconfig.h"
#include "server_localvar.h"
#include "test_pzword.h"
#include "func.h"
#include <memory>
#include <string>
#ifdef ENABLE_OFFICE
#include "pz_word.h"
#endif// ENABLE_OFFICE
namespace http
{
//@urlpath(null,test_pzword)
std::string test_pzword(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_OFFICE
    server_loaclvar &static_server_var = get_server_global_var();

    if (static_server_var.config_path.size() < 5)
    {
        client << "<p> static_server_var.config_path empty </p>";
        return "";
    }

    std::string file_conf = dir_name(static_server_var.config_path);

    if (file_conf.size() > 0 && file_conf.back() != '/')
    {
        file_conf.push_back('/');
    }
    file_conf.append("docs/");

    std::string zipfile;

    zipfile = file_conf + "ww.docx";

    client << "<p>=== Reading " << zipfile << " ===</p></p>";

    pz::word pzw;
    if (!pzw.read(zipfile))
    {
        client << "Read error: " << pzw.error_msg;
        client << "Trying to read from unzipped directory...";

        if (!pzw.read_from_unzipped("."))
        {
            client << "Read from unzipped error: " << pzw.error_msg;
            return "";
        }
        else
        {
            client << "Read from unzipped directory success";
        }
    }
    else
    {
        client << "Read from docx file success";
    }

    std::string html = pzw.to_html();

    std::ofstream html_file(file_conf + "word.html");
    if (html_file)
    {
        html_file << html;
        html_file.close();
        client << "\n=== HTML saved to word.html ===";
    }
    else
    {
        client << "Error: Unable to write word.html";
    }

    pz::word pzww;
    if (!pzww.read_html(file_conf + "word.html"))
    {
        client << "Read HTML error: " << pzww.error_msg;
        return "";
    }

    if (!pzww.write(file_conf + "www.docx"))
    {
        client << "Write DOCX error: " << pzww.error_msg;
        return "";
    }
    client << "<p>Successfully wrote www.docx</p>";
    client << html;
#else
    client << "<p>Please: cmake .. -DENABLE_OFFICE=ON </p>";
#endif// ENABLE_OFFICE

    return "";
}

}// namespace http

5 支持验证码和图片绘文字功能
6 支持SSE 收发,目前可以使用deepseek api
7 添加zip文件 压缩和解压
8 添加新的admin后台,目前是演示,以后慢慢使用新的后台

更多可以看官方地址
https://github.com/hggq/paozhu

 

posted @ 2026-08-08 08:46  游水小鸡  阅读(4)  评论(0)    收藏  举报