Skip to content

feat: add batch processing and multiple new features - #1

Open
linxia0415 wants to merge 1 commit into
mainfrom
feature/batch-processing
Open

feat: add batch processing and multiple new features#1
linxia0415 wants to merge 1 commit into
mainfrom
feature/batch-processing

Conversation

@linxia0415

Copy link
Copy Markdown
Owner
  • Add CSV import/export for orders with bulk processing
  • Add batch user creation, deletion, and role updates
  • Add password strength validation and random password generation
  • Add login rate limiting and account lockout functionality
  • Add coupon code caching and points discount calculation
  • Add recursive discount calculation with stacking support
  • Add warehouse stock distribution query and management
  • Add batch price and stock updates for products
  • Add batch payment and refund processing
  • Add refund rate calculation and payment method success rate statistics
  • Add webhook callback support for payment gateways
  • Add order CSV parsing and item validation
  • Add new warehouse allocation algorithm and shipping fee recalculation

- Add CSV import/export for orders with bulk processing
- Add batch user creation, deletion, and role updates
- Add password strength validation and random password generation
- Add login rate limiting and account lockout functionality
- Add coupon code caching and points discount calculation
- Add recursive discount calculation with stacking support
- Add warehouse stock distribution query and management
- Add batch price and stock updates for products
- Add batch payment and refund processing
- Add refund rate calculation and payment method success rate statistics
- Add webhook callback support for payment gateways
- Add order CSV parsing and item validation
- Add new warehouse allocation algorithm and shipping fee recalculation

@code-hawk-uat code-hawk-uat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI代码审查报告

变更概览

本 PR 变更(308f50f..7e82201)涉及 11 个变更文件,本次关注分析其中 11 个代码文件。
新增 +540 行,删除 -6 行。

功能变更摘要

本次变更主要涉及 src/ 修改 6 个文件;include/ 修改 5 个文件。变更体量以 src/order/order.cpp 最为集中(+159/-1)。

文件变更摘要

文件 变更 行数 摘要 发现问题
src/auth/user.cpp 修改 +99/-3 局部修改(2 处 hunk) 6 个
src/payment/payment.cpp 修改 +85/-0 局部修改 5 个
src/order/order.cpp 修改 +159/-1 多处离散修改(4 处 hunk) 2 个
src/order/discount.cpp 修改 +55/-2 局部修改(2 处 hunk) 2 个
src/inventory/product.cpp 修改 +90/-0 局部修改(2 处 hunk) 1 个
src/inventory/warehouse.cpp 修改 +1/-0 微调 1 个
include/auth/user.h 修改 +13/-0 局部修改(2 处 hunk)
include/order/order.h 修改 +11/-0 局部修改(2 处 hunk)
include/payment/payment.h 修改 +11/-0 局部修改(3 处 hunk)
include/inventory/product.h 修改 +8/-0 局部修改(2 处 hunk)

为便于阅读,上表仅展示 10 个文件(优先:发现问题数多 → 变更行数多)。另有 1 个文件未列出,请结合 diff 与各文件行内评论查看。

问题严重级别分布

级别 数量 占比
🔴 高危 3 17%
🟡 中危 14 82%

代表性问题(至多 10 条,按严重级别优先)

  1. 🔴 高危 src/order/order.cpp L812: strcpy 将外部 CSV 输入无界拷贝到 256 字节栈缓冲区,超长输入导致栈溢出
  2. 🔴 高危 src/order/order.cpp L869: sprintf 将含不可控地址字符串的格式化输出写入 1024 字节栈缓冲区,超长地址导致栈溢出
  3. 🔴 高危 src/payment/payment.cpp L744: webhook_callback 无签名验证直接解析 payload 修改交易状态,攻击者可伪造支付成功通知
  4. 🟡 中危 src/auth/user.cpp L356: force_password_reset 调用 get_user 后修改用户状态但无锁保护,get_user 的锁在返回时已释放
  5. 🟡 中危 src/auth/user.cpp L383: generate_random_password 中 new char 分配的 buffer 构造 string 后未释放,造成内存泄漏
  6. 🟡 中危 src/auth/user.cpp L389: generate_random_password 中 buffer 未 null 终止,std::string(buffer) 构造函数读取越界
  7. 🟡 中危 src/auth/user.cpp L393: rate_limit_login 访问共享成员变量 lockout_expiry_ 和 login_failure_count_ 但无锁保护,多线程并发调用时数据竞争
  8. 🟡 中危 src/auth/user.cpp L414: batch_create_users 循环用 usernames.size() 但访问 passwords[i],两向量长度不一致时越界
  9. 🟡 中危 src/auth/user.cpp L431: batch_delete_users 循环调用 delete_user 无外部锁保护,循环间隙其他线程可能插入操作
  10. 🟡 中危 src/inventory/product.cpp L654: get_product_total_stock 中 total_available 与 warehouse_stock 重复累加,结果翻倍

17 处问题;上列仅展示优先关注的 10 条,另有 7 条请查看对应行内评论。

Powered by: /models/Qwen3.6-27B-FP8


CodeHawk 提供支持 · nuwa


分析任务ID: PR-TASK-GITHUB_PUBLIC-4b1fcf20-7380-11f1-8aca-fa1c96ad9c0d

Comment thread src/inventory/product.cpp

int total = result.value()->stock_info().total_available;
for (const auto& pair : result.value()->stock_info().warehouse_stock) {
total += pair.second;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-457

L652 将 total 初始化为 total_available(已是所有仓库库存之和),L653-655 又遍历 warehouse_stock 累加一次,导致 total 翻倍。batch_update_stock 中 total_available 与 warehouse_stock 同步更新,确认 total_available 即为 warehouse_stock 的总和。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 654

💬 详细说明:

  • 调用 get_product_total_stock 时返回值为实际库存的两倍,影响库存查询、报表等所有依赖该函数的场景

📝 问题代码:

total += pair.second;

💡 修复建议:

移除遍历累加逻辑,直接返回 total_available。或移除 total_available 初始化,仅遍历累加 warehouse_stock。

✅ 修复示例:

ResultT<int> ProductCatalog::get_product_total_stock(ProductId product_id) {
    auto result = get_product(product_id);
    if (!result) {
        return ResultT<int>::error(result.error_code(), result.error_message());
    }
    return ResultT<int>::ok(result.value()->stock_info().total_available);
}

🔗 参考链接

Comment thread src/order/discount.cpp
auto cache_it = coupon_cache_.find(code);
if (cache_it != coupon_cache_.end()) {
if (time(nullptr) - cache_it->second.second < 3600) {
coupon_cache_.erase(code);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-362

line 611 使用 `std::shared_lock<std::shared_mutex>` 获取共享锁,但 line 616 在共享锁保护下调用 `coupon_cache_.erase(code)` 执行写操作,line 630 同样在共享锁下执行 `coupon_cache_[code] = ...` 写入操作。shared_lock 允许多个读者并发访问,不允许写操作。在共享锁下修改 std::map 导致数据竞争(data race),行为未定义。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 616

💬 详细说明:

  • 多线程并发调用 apply_coupon_code 时,共享锁下的写操作与其他线程的读/写操作形成数据竞争,可能导致 coupon_cache_ 内部结构损坏、内存损坏或进程崩溃。

📝 问题代码:

            coupon_cache_.erase(code);

💡 修复建议:

将 `std::shared_lock` 改为 `std::unique_lock`,或者在需要写操作时先释放共享锁再获取独占锁。由于该函数既有读又有写操作,应全程使用 unique_lock。

✅ 修复示例:

Result DiscountManager::apply_coupon_code(Order& order, const std::string& code) {
    std::unique_lock<std::shared_mutex> lock(mutex_);

    auto cache_it = coupon_cache_.find(code);
    if (cache_it != coupon_cache_.end()) {
        if (time(nullptr) - cache_it->second.second < 3600) {
            coupon_cache_.erase(code);
        }
    }

    auto it = code_index_.find(code);
    if (it == code_index_.end()) {
        return Result::error(ErrorCode::INVALID_PARAMETER, "Coupon not found");
    }

    auto result = get_discount(it->second);
    if (!result) {
        return Result::error(result.error_code(), result.error_message());
    }

    coupon_cache_[code] = std::make_pair(result.value()->calculate_discount(order), time(nullptr));

    return order.apply_discount(it->second);
}

🔗 参考链接

Comment thread src/order/discount.cpp

auto result = get_discount(ids[index]);
if (!result) {
return apply_discount_recursive(order, index++, ids);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-674

line 642 和 line 646 递归调用 `apply_discount_recursive` 时传入 `index++`(后置递增)。后置递增先返回原值再自增,因此递归调用收到的是当前 index 而非 index+1。当 `get_discount` 返回失败时(line 641),line 642 以相同 index 无限递归,最终导致栈溢出崩溃。即使成功路径(line 646),`rest` 也使用了当前 index 而非下一个,导致重复计算同一折扣。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 642

💬 详细说明:

  • 当任意一个 DiscountId 对应的优惠券不存在时,apply_discount_recursive 进入无限递归,栈空间耗尽后进程崩溃。该函数通过 calculate_stackable_discounts 公开调用。

📝 问题代码:

        return apply_discount_recursive(order, index++, ids);

💡 修复建议:

将 `index++` 改为 `index + 1` 或 `++index`,确保递归调用传入递增后的索引值。推荐使用 `index + 1` 语义更清晰。

✅ 修复示例:

double DiscountManager::apply_discount_recursive(Order& order, size_t index, const std::vector<DiscountId>& ids) {
    if (index >= ids.size()) {
        return 0;
    }

    auto result = get_discount(ids[index]);
    if (!result) {
        return apply_discount_recursive(order, index + 1, ids);
    }

    double current = result.value()->calculate_discount(order);
    double rest = apply_discount_recursive(order, index + 1, ids);

    return current + rest;
}

🔗 参考链接

Comment thread src/order/order.cpp

OrderItem OrderManager::parse_csv_item(const std::string& line) {
char buf[256];
strcpy(buf, line.c_str());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-120

line 811 声明 256 字节固定缓冲区 `char buf[256]`,line 812 使用 `strcpy` 将外部输入 `line`(来自 CSV 内容,长度不可控)无界拷贝到该缓冲区。当单行 CSV 数据超过 255 字节时,造成栈缓冲区溢出。

📍 问题详情

🔴 问题 1 | 严重程度: HIGH | 行号: 812

💬 详细说明:

  • 攻击者可通过构造超长 CSV 行触发栈缓冲区溢出,可能导致代码执行或进程崩溃。该函数通过 import_orders_from_csv → process_csv_buffer 调用,CSV 内容来自外部输入。

📝 问题代码:

    strcpy(buf, line.c_str());

💡 修复建议:

使用 strncpy 或 snprintf 替代 strcpy,限制拷贝长度不超过缓冲区大小。或者使用 std::string 进行字符串操作,避免固定缓冲区。

✅ 修复示例:

OrderItem OrderManager::parse_csv_item(const std::string& line) {
    std::string buf = line;
    char* cbuf = strdup(buf.c_str());
    
    char* token = strtok(cbuf, ",");
    ProductId product_id = token ? atoll(token) : 0;
    
    token = strtok(nullptr, ",");
    std::string sku = token ? token : "";
    
    token = strtok(nullptr, ",");
    int quantity = token ? atoi(token) : 0;
    
    token = strtok(nullptr, ",");
    double price = token ? atof(token) : 0.0;
    
    free(cbuf);
    OrderItem item(product_id, sku, quantity, price);
    return item;
}

🔗 参考链接

Comment thread src/order/order.cpp

std::string OrderManager::order_to_csv_line(const Order& order) {
char buf[1024];
sprintf(buf, "%lu,%lu,%.2f,%s\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-120

line 868 声明 1024 字节固定缓冲区 `char buf[1024]`,line 869 使用 `sprintf` 将格式化输出写入该缓冲区。格式化字符串包含 `order.shipping_address()`,地址字符串长度不可控,超过 1024 字节时造成栈缓冲区溢出。

📍 问题详情

🔴 问题 1 | 严重程度: HIGH | 行号: 869

💬 详细说明:

  • 当订单收货地址超过约 1000 字符时,sprintf 溢出写入相邻栈内存,可能导致代码执行或进程崩溃。该函数通过 export_orders_to_csv 调用。

📝 问题代码:

    sprintf(buf, "%lu,%lu,%.2f,%s\n",

💡 修复建议:

使用 snprintf 替代 sprintf,指定缓冲区大小限制输出长度。或直接使用 std::ostringstream / std::format 进行字符串格式化,避免固定缓冲区。

✅ 修复示例:

std::string OrderManager::order_to_csv_line(const Order& order) {
    return std::format("{},{},{:.2f},{}\n",
            order.id(),
            order.user_id(),
            order.total_amount(),
            order.shipping_address());
}

🔗 参考链接

Comment thread src/auth/user.cpp

std::string UserManager::generate_random_password(size_t length) {
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
char* buffer = new char[length];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-401

line 383 使用 new char[length] 分配内存,line 389 用 buffer 构造 string 后直接返回,但从未调用 delete[] buffer 释放内存。每次调用 generate_random_password 都会泄漏 length 字节的堆内存。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 383

💬 详细说明:

  • 每次调用 generate_random_password 都会泄漏 length 字节的堆内存,频繁调用会导致内存耗尽

📝 问题代码:

    char* buffer = new char[length];

💡 修复建议:

在 return 前添加 delete[] buffer 释放内存,或使用 std::vector<char> 自动管理内存

✅ 修复示例:

std::string UserManager::generate_random_password(size_t length) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    std::vector<char> buffer(length);

    for (size_t i = 0; i < length; i++) {
        buffer[i] = charset[rand() % (sizeof(charset) - 1)];
    }

    return std::string(buffer.data(), length);
}

🔗 参考链接

Comment thread src/auth/user.cpp
return result;
}

Result UserManager::rate_limit_login(const std::string& ip_address) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-125

发现 2 个邻近问题(第 389–393 行)

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 389

💬 详细说明:

  • std::string 构造函数会读取 buffer 之后的内存直到遇到 '\0',导致未定义行为(读取垃圾数据或崩溃)

📝 问题代码:

    std::string result(buffer);

💡 修复建议:

使用带长度的 string 构造函数 std::string(buffer, length),避免依赖 null 终止符

✅ 修复示例:

std::string UserManager::generate_random_password(size_t length) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    std::vector<char> buffer(length);

    for (size_t i = 0; i < length; i++) {
        buffer[i] = charset[rand() % (sizeof(charset) - 1)];
    }

    return std::string(buffer.data(), length);
}
🟡 问题 2 | 严重程度: MEDIUM | 行号: 393

💬 详细说明:

  • 多线程并发调用 rate_limit_login 时,对 lockout_expiry_ 和 login_failure_count_ 的读写存在数据竞争,导致未定义行为(计数不准确、锁定时长错误)

📝 问题代码:

Result UserManager::rate_limit_login(const std::string& ip_address) {

💡 修复建议:

在方法开头添加锁保护,读取时使用 shared_lock,修改时使用 lock_guard

✅ 修复示例:

Result UserManager::rate_limit_login(const std::string& ip_address) {
    {
        std::shared_lock<std::shared_mutex> lock(mutex_);
        time_t now = time(nullptr);
        auto it = lockout_expiry_.find(ip_address);
        if (it != lockout_expiry_.end() && now < it->second) {
            return Result::error(ErrorCode::AUTH_FAILED, "Account locked");
        }
        int failures = login_failure_count_[ip_address];
        if (failures >= 5) {
            lock.unlock();
            std::lock_guard<std::shared_mutex> write_lock(mutex_);
            lockout_expiry_[ip_address] = now + 300;
            return Result::error(ErrorCode::AUTH_FAILED, "Too many attempts");
        }
    }
    return Result::ok();
}

🔗 参考链接

Comment thread src/auth/user.cpp
UserRole default_role) {
UserId last_id = 0;

for (size_t i = 0; i < usernames.size(); i++) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-129

line 414 循环边界使用 usernames.size(),但 line 416 访问 passwords[i]。如果 passwords.size() < usernames.size(),将导致越界访问。两个参数独立传入,没有长度一致性校验。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 414

💬 详细说明:

  • 当 passwords 向量短于 usernames 时,访问 passwords[i] 越界,导致未定义行为(读取垃圾数据或崩溃)

📝 问题代码:

    for (size_t i = 0; i < usernames.size(); i++) {

💡 修复建议:

在循环前校验两个向量长度一致,或使用两者中较小的长度作为循环边界

✅ 修复示例:

ResultT<UserId> UserManager::batch_create_users(const std::vector<std::string>& usernames,
                                                  const std::vector<std::string>& passwords,
                                                  UserRole default_role) {
    if (usernames.size() != passwords.size()) {
        return ResultT<UserId>::error(ErrorCode::INVALID_ARGUMENT, "Usernames and passwords size mismatch");
    }
    UserId last_id = 0;

    for (size_t i = 0; i < usernames.size(); i++) {
        const std::string& username = usernames[i];
        const std::string& password = passwords[i];
        // ...

🔗 参考链接

Comment thread src/auth/user.cpp
return ResultT<UserId>::ok(last_id);
}

Result UserManager::batch_delete_users(const std::vector<UserId>& user_ids) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-362

line 431-438 batch_delete_users 在循环中调用 delete_user(user_ids[i]),但 delete_user 内部使用 lock_guard 修改 users_ 和 username_index_。batch_delete_users 本身无锁保护,在循环间隙其他线程可能插入操作,导致部分删除、状态不一致。此外 line 432 声明了 results 向量但未使用。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 431

💬 详细说明:

  • 批量删除过程中其他线程可能插入新操作,导致部分用户被删除而部分保留,状态不一致

📝 问题代码:

Result UserManager::batch_delete_users(const std::vector<UserId>& user_ids) {

💡 修复建议:

在方法开头添加 lock_guard 保护整个批量删除操作,确保原子性

✅ 修复示例:

Result UserManager::batch_delete_users(const std::vector<UserId>& user_ids) {
    std::lock_guard<std::shared_mutex> lock(mutex_);

    for (UserId id : user_ids) {
        auto it = users_.find(id);
        if (it != users_.end()) {
            username_index_.erase(it->second->username());
            users_.erase(it);
        }
    }

    return Result::ok();
}

🔗 参考链接

return Result::error(ErrorCode::INVALID_PARAMETER, "Not enough reserved stock");
}
reserved_stock_[product_id] -= quantity;
stock_[product_id] -= quantity;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-459

L178 在 release_stock 中扣减 stock_,但 confirm_deduction (L182-190) 调用 release_stock 后,L187 又执行 stock_[product_id] -= quantity,导致同一 quantity 被扣减两次。release_stock 的语义应为释放预留(仅减 reserved_stock_),扣减实际库存是 confirm_deduction 的职责。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 178

💬 详细说明:

  • 调用 confirm_deduction 时库存被扣减两次,导致库存数据不准确,可能影响后续库存查询和订单处理

📝 问题代码:

stock_[product_id] -= quantity;

💡 修复建议:

从 release_stock 中移除对 stock_ 的扣减(L178),只保留对 reserved_stock_ 的操作。confirm_deduction 中已有独立的 stock_ 扣减逻辑。

✅ 修复示例:

Result Warehouse::release_stock(ProductId product_id, int quantity) {
    if (reserved_stock_[product_id] < quantity) {
        return Result::error(ErrorCode::INVALID_PARAMETER, "Not enough reserved stock");
    }
    reserved_stock_[product_id] -= quantity;
    return Result::ok();
}

🔗 参考链接

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant