Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
-- V14__Add_summary_tables.sql
-- Add product_summary and product_like tables to support product recommendation
-- Description: support product recommendation sorting by usage, likes and subscriptions

START TRANSACTION;

-- ========================================
-- product_summary table
-- ========================================
CREATE TABLE IF NOT EXISTS `product_summary` (
`id` bigint NOT NULL AUTO_INCREMENT,
`product_id` varchar(64) NOT NULL,
`name` varchar(64) NOT NULL,
`type` varchar(64) DEFAULT NULL,
`description` varchar(1000) DEFAULT NULL,
`icon` json DEFAULT NULL,
`usage_count` bigint NOT NULL,
`likes_count` bigint NOT NULL,
`subscription_count` bigint NOT NULL,
`created_at` datetime(3) DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ========================================
-- product_like table
-- ========================================
CREATE TABLE IF NOT EXISTS `product_like` (
`id` bigint NOT NULL AUTO_INCREMENT,
`developer_id` varchar(64) DEFAULT NULL,
`like_id` varchar(64) DEFAULT NULL,
`portal_id` varchar(64) DEFAULT NULL,
`product_id` varchar(64) NOT NULL,
`status` varchar(64) DEFAULT NULL,
`created_at` datetime(3) DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_product_consumer` (`product_id`, `developer_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


-- Delete duplicate data, keep the record with the smallest id
DELETE ps1 FROM product_summary ps1
INNER JOIN product_summary ps2
ON ps1.product_id = ps2.product_id
AND ps1.id > ps2.id;

-- Add unique constraint
ALTER TABLE `product_summary` ADD UNIQUE KEY `uk_product_id` (`product_id`);

-- Add description column
ALTER TABLE product_summary MODIFY COLUMN description VARCHAR(1000) DEFAULT NULL;

COMMIT;

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.alibaba.himarket.entity;

import com.alibaba.himarket.support.enums.LikeStatus;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Entity
@Table(
name = "product_like",
uniqueConstraints = {
@UniqueConstraint(
columnNames = {"product_id", "developer_id"},
name = "uk_product_developer")
})
@Data
@EqualsAndHashCode(callSuper = true)
public class ProductLike extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "like_id", length = 64, unique = true)
private String likeId;

@Column(name = "product_id", length = 64, nullable = false)
private String productId;

@Column(name = "developer_id", length = 64)
private String developerId;

@Column(name = "portal_id", length = 64)
private String portalId;

@Column(name = "status", length = 32)
@Enumerated(EnumType.STRING)
private LikeStatus status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.alibaba.himarket.entity;

import com.alibaba.himarket.converter.IconConverter;
import com.alibaba.himarket.support.enums.ProductType;
import com.alibaba.himarket.support.product.Icon;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "product_summary")
@Data
public class ProductSummary extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "product_id", length = 64, nullable = false)
private String productId;

@Column(name = "name", length = 64, nullable = false)
private String name;

@Column(name = "type", length = 64)
@Enumerated(EnumType.STRING)
private ProductType type;

@Column(name = "description", length = 1000)
private String description;

@Column(name = "icon", columnDefinition = "json")
@Convert(converter = IconConverter.class)
private Icon icon;

@Column(name = "subscription_count", nullable = false)
private Long subscriptionCount;

@Column(name = "usage_count", nullable = false)
private Long usageCount;

@Column(name = "likes_count", nullable = false)
private Long likesCount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,23 @@ Integer findCurrentSequence(
* @param sessionId the session ID
*/
void deleteAllBySessionId(String sessionId);

/**
* Count chats grouped by productId
*
* @return List of Object[] where index 0 is productId and index 1 is count
*/
@Query(
"SELECT c.productId, COUNT(c) FROM Chat c WHERE c.productId IS NOT NULL GROUP BY"
+ " c.productId")
List<Object[]> countChatsGroupedByProductId();

/**
* Count chats for specific productId
*
* @param productId
* @return count of chats for the product
*/
@Query("SELECT COUNT(c) FROM Chat c WHERE c.productId = :productId")
Long countChatsByProductId(@Param("productId") String productId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.alibaba.himarket.repository;

import com.alibaba.himarket.entity.ProductLike;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

/**
* Repository interface for managing product like operations.
* Provides methods for querying, counting, and managing product like records.
*/
public interface ProductLikeRepository extends JpaRepository<ProductLike, Long> {

/**
* Finds a like record by product ID and developer ID
*
* @param productId the product identifier
* @param developerId the developer identifier
* @return optional like record
*/
Optional<ProductLike> findByProductIdAndDeveloperId(String productId, String developerId);

/**
* Deletes all like records associated with a product
*
* @param productId the product identifier
*/
void deleteAllByProductId(String productId);

/**
* Counts the number of likes for a specific product
*
* @param productId the product identifier
* @return count of likes for the product
*/
@Query(
"SELECT COUNT(pl) FROM ProductLike pl WHERE pl.productId = :productId AND pl.status ="
+ " com.alibaba.himarket.support.enums.LikeStatus.LIKED")
Long countByProductIdAndStatus(@Param("productId") String productId);

/**
* Counts likes grouped by product ID
*
* @return List of Object[] where index 0 is productId and index 1 is like count
*/
@Query(
"SELECT pl.productId, COUNT(pl) FROM ProductLike pl WHERE pl.status ="
+ " com.alibaba.himarket.support.enums.LikeStatus.LIKED GROUP BY pl.productId")
List<Object[]> countLikesGroupedByProductId();

/**
* Atomically upsert a like record using INSERT ... ON DUPLICATE KEY UPDATE.
* When the record already exists, toggles the status between LIKED and UNLIKED.
*
* @param likeId unique identifier for a new like record
* @param productId the product identifier
* @param developerId the developer identifier
* @param portalId the portal identifier
*/
@Modifying
@Query(
value =
"INSERT INTO product_like (like_id, product_id, developer_id, portal_id,"
+ " status, created_at, updated_at) VALUES (:likeId, :productId,"
+ " :developerId, :portalId, 'LIKED', NOW(), NOW()) ON DUPLICATE KEY UPDATE"
+ " status = CASE WHEN status = 'LIKED' THEN 'UNLIKED' ELSE 'LIKED' END,"
+ " updated_at = NOW()",
nativeQuery = true)
int upsertLike(
@Param("likeId") String likeId,
@Param("productId") String productId,
@Param("developerId") String developerId,
@Param("portalId") String portalId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
package com.alibaba.himarket.repository;

import com.alibaba.himarket.entity.Product;
import com.alibaba.himarket.support.enums.ProductStatus;
import com.alibaba.himarket.support.enums.ProductType;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

@Repository
Expand Down Expand Up @@ -61,4 +64,13 @@ public interface ProductRepository extends BaseRepository<Product, Long> {
* @return the list of products
*/
List<Product> findAllByType(ProductType type);

/**
* Find products by status
*
* @param status the product status
* @param pageable the pageable
* @return the page of products
*/
Page<Product> findAllByStatus(ProductStatus status, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.alibaba.himarket.repository;

import com.alibaba.himarket.entity.ProductSummary;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductSummaryRepository
extends JpaRepository<ProductSummary, Long>, JpaSpecificationExecutor<ProductSummary> {
void deleteByProductId(String productId);

Optional<ProductSummary> findByProductId(String productId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.alibaba.himarket.entity.ProductSubscription;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface SubscriptionRepository extends BaseRepository<ProductSubscription, Long> {

Expand Down Expand Up @@ -79,4 +81,14 @@ public interface SubscriptionRepository extends BaseRepository<ProductSubscripti
* @param productId the product ID
*/
void deleteByConsumerIdAndProductId(String consumerId, String productId);

@Query(
"SELECT s.productId, COUNT(s) FROM ProductSubscription s WHERE s.status = 'APPROVED'"
+ " GROUP BY s.productId")
List<Object[]> countApprovedSubscriptionsGroupedByProductId();

@Query(
"SELECT COUNT(s) FROM ProductSubscription s WHERE s.status = 'APPROVED'"
+ " AND s.productId = :productId")
Long countApprovedSubscriptionsByProductId(@Param("productId") String productId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.alibaba.himarket.support.enums;

public enum LikeStatus {
LIKED,
UNLIKED;
}
Loading
Loading