Skip to content

feat: add HiMarket Product supports Recommendation #69#197

Open
dexchong wants to merge 3 commits intohigress-group:mainfrom
dexchong:feat/recs260320
Open

feat: add HiMarket Product supports Recommendation #69#197
dexchong wants to merge 3 commits intohigress-group:mainfrom
dexchong:feat/recs260320

Conversation

@dexchong
Copy link
Contributor

@dexchong dexchong commented Mar 20, 2026

Description feat: add HiMarket Product supports Recommendation

  • 新增门户产品列表接口 - ProductSummaryController支持订阅量、点赞量排序,兼容性旧接口
  • product_summary 产品统计维度表:包含 subscriptionCount、usageCount、likesCount 统计字段
  • product_like 点赞表:支持点赞和取消点赞,异步更新 product_summary
  • 新增product_subscription 、product_like 表的订阅变更事件
  • 新增后台产品统计菜单:提供了一键同步功能,可同步产品以及订阅量、点赞量维度的统计

3️⃣ Type of Change (Required)

  • feat

Related Issues

✅ Type of Change

  • New feature (non-breaking change which adds functionality)

📋 Checklist

  • Code has been formatted (mvn spotless:apply for backend, npm run lint:fix for frontend)
  • Code is self-reviewed

@dexchong
Copy link
Contributor Author

@lexburner 冲突已修复

@lexburner
Copy link
Contributor

lexburner commented Mar 20, 2026

非常感谢 @dexchong 的贡献,这个功能对于产品推荐非常有价值,社区也有用户反馈过需要该功能。

我拉取了分支进行了本地测试,在存量数据的情况下,页面访问报错,需要修复下。

🔴 报错原因

访问产品列表接口时抛出异常:

java.lang.IllegalStateException: Duplicate key product-69849d3d45b290265f317923 
(attempted merging values ProductSummary(id=1, productId=...) and ProductSummary(id=2, productId=...))
    at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:135)
    at com.alibaba.himarket.service.impl.ProductSummaryServiceImpl.syncAllProductSummary(ProductSummaryServiceImpl.java:120)

原因是 product_summary 表中存在重复的 product_id 记录(本地数据库有 17 个产品存在此问题),而代码未处理这种场景。


Code Review 建议

🔴 严重问题(必须修复)

1. product_summary 表缺少 product_id 唯一约束

位置: V14__Add_summary_tables.sql:10-24

CREATE TABLE IF NOT EXISTS `product_summary` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `product_id` varchar(64) NOT NULL,
    -- 没有 UNIQUE KEY 约束!
    PRIMARY KEY (`id`)
);

建议: 添加唯一约束,并在迁移脚本中清理可能存在的重复数据:

-- 先删除重复数据,保留 id 最小的记录
DELETE ps1 FROM product_summary ps1
INNER JOIN product_summary ps2 
WHERE ps1.product_id = ps2.product_id AND ps1.id > ps2.id;

-- 添加唯一约束
ALTER TABLE `product_summary` ADD UNIQUE KEY `uk_product_id` (`product_id`);

2. syncAllProductSummary 方法未处理重复 key

位置: ProductSummaryServiceImpl.java:118-120

Map<String, ProductSummary> existingStatsMap =
    existingStats.stream()
        .collect(Collectors.toMap(ProductSummary::getProductId, s -> s));

建议: 使用 merge function 处理重复 key:

.collect(Collectors.toMap(
    ProductSummary::getProductId, 
    s -> s, 
    (existing, duplicate) -> existing)); // 保留第一条

3. ProductLikeController 缺少认证注解

位置: ProductLikeController.java:35-38

@RestController
@RequestMapping("/product-like")
public class ProductLikeController {
    // 没有 @AdminAuth 或 @DeveloperAuth 注解!

问题: 点赞接口没有认证,任何人都可以调用,存在安全隐患。

建议: 添加 @DeveloperAuth 注解:

@DeveloperAuth
@RestController
@RequestMapping("/product-like")
public class ProductLikeController {

4. ProductSummaryController.listProducts 缺少认证

位置: ProductSummaryController.java:27-31

建议: 根据业务需求添加适当的认证注解(如 @AdminOrDeveloperAuth)。


🟡 中等问题

5. 数据库迁移文件注释错误

位置: V14__Add_summary_tables.sql:1-3

-- V3__Add_chat_tables.sql  <-- 错误!复制粘贴遗留问题

6. SQL 与 Entity 字段不一致

字段 SQL 定义 Entity 定义
admin_id 存在 不存在
usage_count int Long
likes_count int Long

7. 查询不存在的字段 status

位置: ProductSummaryServiceImpl.java:270-272

if (param.getStatus() != null) {
    predicates.add(cb.equal(root.get("status"), param.getStatus()));
}

问题: ProductSummary 实体没有 status 字段,运行时会报错。


8. ProductSummaryUpdateEvent.UpdateType 未被使用

syncProductSummary 方法总是同步所有字段,没有根据 updateType 做增量更新,可以考虑移除或实现增量更新逻辑。


🟢 建议优化

9. 性能问题:每次查询都检查同步

位置: ProductSummaryServiceImpl.java:232-234

if (productSummaryRepository.count() != productRepository.count()) {
    syncAllProductSummary();
}

每次 listProducts 都会执行两次 count 查询,建议使用定时任务同步,而不是在查询时触发。


10. 事件发布时机问题

位置: ProductLikeServiceImpl.java:76-79

事件在 save 之前发布,如果 save 失败,事件已经发布了。建议将事件发布移到 save 之后。


📋 问题汇总

级别 数量 说明
🔴 严重 4 必须修复
🟡 中等 4 建议修复
🟢 建议 2 可选优化

希望以上反馈对您有帮助,期待您的更新!🤝 Generated with Qoder

- Unique constraint on product_summary
- Remove UpdateType enum (unused)
- Like button in SkillDetail page
@lexburner
Copy link
Contributor

代码审查报告

感谢贡献!PR 整体功能完整,但发现以下问题需要修复:

🔴 P0 - 必须修复

  1. 重复的事件监听方法 (ProductSummaryServiceImpl.java:363-377)

    • handleProductSummaryDeletionByProducthandleProductSummaryDeletion 两个方法完全相同
    • 请删除其中一个
  2. 并发安全问题 (ProductLikeServiceImpl.java:44-76)

    • toggleLike 方法存在竞态条件
    • 建议:使用数据库 UPSERT 或应用层分布式锁
  3. 数据类型不一致 (V14__Add_summary_tables.sql:19)

    • subscription_countint,其他 count 是 bigint
    • 建议统一为 bigint

🟡 P1 - 建议修复

  1. 全表扫描性能问题 (ProductSummaryServiceImpl.java:137)

    • syncAllProductSummary 使用 findAll() 全表扫描
    • 建议分页处理
  2. 手动事件发布 (ProductLikeServiceImpl.java:76)

    • 使用 SpringUtil.getApplicationContext() 紧耦合
    • 建议注入 ApplicationEventPublisher

✅ 优点

  • 数据库唯一约束设计合理
  • 批量处理优化(每批 100 条)
  • 事件驱动架构解耦
  • 前端 UX 完善

修复建议代码

问题 2 修复示例(使用 INSERT ON DUPLICATE KEY UPDATE):

@Query(value = "INSERT INTO product_like (like_id, product_id, developer_id, portal_id, status, created_at, updated_at) " +
        "VALUES (:likeId, :productId, :developerId, :portalId, :status, NOW(), NOW()) " +
        "ON DUPLICATE KEY UPDATE status = :status, updated_at = NOW()", nativeQuery = true)
@Modifying
int upsertLike(...);

请修复 P0 问题后重新提交,我会再次 review。

cc @lexburner


Saber 酱 🤖

- Add atomic upsertLike
- ProductSummary:Align page param
- Rename listener unPublishProduct
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.

2 participants