feat: add batch processing and multiple new features - #1
Conversation
linxia0415
commented
Jun 29, 2026
- 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
There was a problem hiding this comment.
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 条,按严重级别优先)
- 🔴 高危
src/order/order.cppL812: strcpy 将外部 CSV 输入无界拷贝到 256 字节栈缓冲区,超长输入导致栈溢出 - 🔴 高危
src/order/order.cppL869: sprintf 将含不可控地址字符串的格式化输出写入 1024 字节栈缓冲区,超长地址导致栈溢出 - 🔴 高危
src/payment/payment.cppL744: webhook_callback 无签名验证直接解析 payload 修改交易状态,攻击者可伪造支付成功通知 - 🟡 中危
src/auth/user.cppL356: force_password_reset 调用 get_user 后修改用户状态但无锁保护,get_user 的锁在返回时已释放 - 🟡 中危
src/auth/user.cppL383: generate_random_password 中 new char 分配的 buffer 构造 string 后未释放,造成内存泄漏 - 🟡 中危
src/auth/user.cppL389: generate_random_password 中 buffer 未 null 终止,std::string(buffer) 构造函数读取越界 - 🟡 中危
src/auth/user.cppL393: rate_limit_login 访问共享成员变量 lockout_expiry_ 和 login_failure_count_ 但无锁保护,多线程并发调用时数据竞争 - 🟡 中危
src/auth/user.cppL414: batch_create_users 循环用 usernames.size() 但访问 passwords[i],两向量长度不一致时越界 - 🟡 中危
src/auth/user.cppL431: batch_delete_users 循环调用 delete_user 无外部锁保护,循环间隙其他线程可能插入操作 - 🟡 中危
src/inventory/product.cppL654: 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
|
|
||
| int total = result.value()->stock_info().total_available; | ||
| for (const auto& pair : result.value()->stock_info().warehouse_stock) { | ||
| total += pair.second; |
There was a problem hiding this comment.
🟡 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);
}
🔗 参考链接
无
| 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); |
There was a problem hiding this comment.
🟡 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);
}
🔗 参考链接
无
|
|
||
| auto result = get_discount(ids[index]); | ||
| if (!result) { | ||
| return apply_discount_recursive(order, index++, ids); |
There was a problem hiding this comment.
🟡 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;
}
🔗 参考链接
无
|
|
||
| OrderItem OrderManager::parse_csv_item(const std::string& line) { | ||
| char buf[256]; | ||
| strcpy(buf, line.c_str()); |
There was a problem hiding this comment.
🔴 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;
}
🔗 参考链接
无
|
|
||
| std::string OrderManager::order_to_csv_line(const Order& order) { | ||
| char buf[1024]; | ||
| sprintf(buf, "%lu,%lu,%.2f,%s\n", |
There was a problem hiding this comment.
🔴 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());
}
🔗 参考链接
无
|
|
||
| std::string UserManager::generate_random_password(size_t length) { | ||
| const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | ||
| char* buffer = new char[length]; |
There was a problem hiding this comment.
🟡 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);
}
🔗 参考链接
无
| return result; | ||
| } | ||
|
|
||
| Result UserManager::rate_limit_login(const std::string& ip_address) { |
There was a problem hiding this comment.
🟡 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();
}
🔗 参考链接
无
| UserRole default_role) { | ||
| UserId last_id = 0; | ||
|
|
||
| for (size_t i = 0; i < usernames.size(); i++) { |
There was a problem hiding this comment.
🟡 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];
// ...
🔗 参考链接
无
| return ResultT<UserId>::ok(last_id); | ||
| } | ||
|
|
||
| Result UserManager::batch_delete_users(const std::vector<UserId>& user_ids) { |
There was a problem hiding this comment.
🟡 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; |
There was a problem hiding this comment.
🟡 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();
}
🔗 参考链接
无