diff --git a/.gitignore b/.gitignore index 0b0008a..33f4513 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ build/ ### VS Code ### .vscode/ /CLAUDE.md +/AGENTS.md diff --git a/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java b/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java index 018f8bc..ae47f8a 100644 --- a/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java +++ b/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java @@ -93,7 +93,12 @@ public class SaTokenConfigure implements WebMvcConfigurer { "/themes/purchase/list", "/themes/detail", "/themes/recommended", - "/user-themes/batch-delete" + "/user-themes/batch-delete", + "/products/listByType", + "/products/detail", + "/products/inApp/list", + "/products/subscription/list" + }; } @Bean diff --git a/src/main/java/com/yolo/keyborad/controller/ProductsController.java b/src/main/java/com/yolo/keyborad/controller/ProductsController.java new file mode 100644 index 0000000..735b49e --- /dev/null +++ b/src/main/java/com/yolo/keyborad/controller/ProductsController.java @@ -0,0 +1,71 @@ +package com.yolo.keyborad.controller; + +import com.yolo.keyborad.common.BaseResponse; +import com.yolo.keyborad.common.ErrorCode; +import com.yolo.keyborad.common.ResultUtils; +import com.yolo.keyborad.exception.BusinessException; +import com.yolo.keyborad.model.vo.products.KeyboardProductItemRespVO; +import com.yolo.keyborad.service.KeyboardProductItemsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/* + * @author: ziin + * @date: 2025/12/12 + */ +@RestController +@Slf4j +@RequestMapping("/products") +@Tag(name = "商品", description = "商品相关接口") +public class ProductsController { + + @Resource + private KeyboardProductItemsService productItemsService; + + @GetMapping("/detail") + @Operation(summary = "查询商品明细", description = "根据商品ID或productId查询商品详情") + public BaseResponse getProductDetail( + @RequestParam(value = "id", required = false) Long id, + @RequestParam(value = "productId", required = false) String productId + ) { + if (id == null && (productId == null || productId.isBlank())) { + throw new BusinessException(ErrorCode.PARAMS_ERROR, "id 或 productId 至少传一个"); + } + KeyboardProductItemRespVO result = (id != null) + ? productItemsService.getProductDetailById(id) + : productItemsService.getProductDetailByProductId(productId); + return ResultUtils.success(result); + } + + @GetMapping("/listByType") + @Operation(summary = "按类型查询商品列表", description = "根据商品类型查询商品列表,type=all 返回全部") + public BaseResponse> listByType(@RequestParam("type") String type) { + if (type == null || type.isBlank()) { + throw new BusinessException(ErrorCode.PARAMS_ERROR, "type 不能为空"); + } + List result = productItemsService.listProductsByType(type); + return ResultUtils.success(result); + } + + @GetMapping("/inApp/list") + @Operation(summary = "查询内购商品列表", description = "查询 type=in-app-purchase 的商品列表") + public BaseResponse> listInAppPurchases() { + List result = productItemsService.listProductsByType("in-app-purchase"); + return ResultUtils.success(result); + } + + @GetMapping("/subscription/list") + @Operation(summary = "查询订阅商品列表", description = "查询 type=subscription 的商品列表") + public BaseResponse> listSubscriptions() { + List result = productItemsService.listProductsByType("subscription"); + return ResultUtils.success(result); + } +} diff --git a/src/main/java/com/yolo/keyborad/mapper/KeyboardProductItemsMapper.java b/src/main/java/com/yolo/keyborad/mapper/KeyboardProductItemsMapper.java new file mode 100644 index 0000000..6c88e4b --- /dev/null +++ b/src/main/java/com/yolo/keyborad/mapper/KeyboardProductItemsMapper.java @@ -0,0 +1,12 @@ +package com.yolo.keyborad.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yolo.keyborad.model.entity.KeyboardProductItems; + +/* +* @author: ziin +* @date: 2025/12/12 13:44 +*/ + +public interface KeyboardProductItemsMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/model/entity/KeyboardProductItems.java b/src/main/java/com/yolo/keyborad/model/entity/KeyboardProductItems.java new file mode 100644 index 0000000..e4b75d7 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/entity/KeyboardProductItems.java @@ -0,0 +1,111 @@ +package com.yolo.keyborad.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Data; + +/* +* @author: ziin +* @date: 2025/12/12 13:44 +*/ + +@Schema +@Data +@TableName(value = "keyboard_product_items") +public class KeyboardProductItems { + /** + * 主键,自增,唯一标识每个产品项 + */ + @TableId(value = "id", type = IdType.AUTO) + @Schema(description="主键,自增,唯一标识每个产品项") + private Long id; + + /** + * 产品标识符,唯一标识每个产品(如 com.loveKey.nyx.2month) + */ + @TableField(value = "product_id") + @Schema(description="产品标识符,唯一标识每个产品(如 com.loveKey.nyx.2month)") + private String productId; + + /** + * 产品类型,区分订阅(subscription)和内购(in-app-purchase) + */ + @TableField(value = "\"type\"") + @Schema(description="产品类型,区分订阅(subscription)和内购(in-app-purchase)") + private String type; + + /** + * 产品名称(如 100, 2) + */ + @TableField(value = "\"name\"") + @Schema(description="产品名称(如 100, 2)") + private String name; + + /** + * 产品单位(如 金币,个月) + */ + @TableField(value = "unit") + @Schema(description="产品单位(如 金币,个月)") + private String unit; + + /** + * 订阅时长的数值部分(如 2) + */ + @TableField(value = "duration_value") + @Schema(description="订阅时长的数值部分(如 2)") + private Integer durationValue; + + /** + * 订阅时长的单位部分(如 月,天) + */ + @TableField(value = "duration_unit") + @Schema(description="订阅时长的单位部分(如 月,天)") + private String durationUnit; + + /** + * 产品价格 + */ + @TableField(value = "price") + @Schema(description="产品价格") + private BigDecimal price; + + /** + * 产品的货币单位,如美元($) + */ + @TableField(value = "currency") + @Schema(description="产品的货币单位,如美元($)") + private String currency; + + /** + * 产品的描述,提供更多细节信息 + */ + @TableField(value = "description") + @Schema(description="产品的描述,提供更多细节信息") + private String description; + + /** + * 产品项的创建时间,默认当前时间 + */ + @TableField(value = "created_at") + @Schema(description="产品项的创建时间,默认当前时间") + private Date createdAt; + + /** + * 产品项的最后更新时间,更新时自动设置为当前时间 + */ + @TableField(value = "updated_at") + @Schema(description="产品项的最后更新时间,更新时自动设置为当前时间") + private Date updatedAt; + + /** + * 订阅时长的具体天数 + */ + @TableField(value = "duration_days") + @Schema(description="订阅时长的具体天数") + private Integer durationDays; +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/model/vo/products/KeyboardProductItemRespVO.java b/src/main/java/com/yolo/keyborad/model/vo/products/KeyboardProductItemRespVO.java new file mode 100644 index 0000000..146879d --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/vo/products/KeyboardProductItemRespVO.java @@ -0,0 +1,48 @@ +package com.yolo.keyborad.model.vo.products; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import lombok.Data; + +/** + * 商品明细返回 VO + */ +@Data +@Schema(description = "商品明细返回对象") +public class KeyboardProductItemRespVO { + + @Schema(description = "主键ID") + private Long id; + + @Schema(description = "产品标识符,如 com.loveKey.nyx.2month") + private String productId; + + @Schema(description = "产品类型:subscription / in-app-purchase") + private String type; + + @Schema(description = "产品名称") + private String name; + + @Schema(description = "产品单位") + private String unit; + + @Schema(description = "订阅时长数值") + private Integer durationValue; + + @Schema(description = "订阅时长单位") + private String durationUnit; + + @Schema(description = "订阅时长天数") + private Integer durationDays; + + @Schema(description = "价格") + private BigDecimal price; + + @Schema(description = "货币单位") + private String currency; + + @Schema(description = "描述") + private String description; + +} + diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardProductItemsService.java b/src/main/java/com/yolo/keyborad/service/KeyboardProductItemsService.java new file mode 100644 index 0000000..8a2f7f1 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/KeyboardProductItemsService.java @@ -0,0 +1,39 @@ +package com.yolo.keyborad.service; + +import com.yolo.keyborad.model.entity.KeyboardProductItems; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yolo.keyborad.model.vo.products.KeyboardProductItemRespVO; +import java.util.List; +/* +* @author: ziin +* @date: 2025/12/12 13:44 +*/ + +public interface KeyboardProductItemsService extends IService{ + + /** + * 根据主键ID查询商品明细 + * + * @param id 商品主键ID + * @return 商品明细(不存在返回 null) + */ + KeyboardProductItemRespVO getProductDetailById(Long id); + + /** + * 根据 Apple productId 查询商品明细 + * + * @param productId 商品 productId + * @return 商品明细(不存在返回 null) + */ + KeyboardProductItemRespVO getProductDetailByProductId(String productId); + + /** + * 根据商品类型查询商品列表 + * type=all 时返回全部 + * + * @param type 商品类型:subscription / in-app-purchase / all + * @return 商品列表 + */ + List listProductsByType(String type); + +} diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardProductItemsServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardProductItemsServiceImpl.java new file mode 100644 index 0000000..cb58a70 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardProductItemsServiceImpl.java @@ -0,0 +1,89 @@ +package com.yolo.keyborad.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.yolo.keyborad.model.vo.products.KeyboardProductItemRespVO; +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yolo.keyborad.model.entity.KeyboardProductItems; +import com.yolo.keyborad.mapper.KeyboardProductItemsMapper; +import com.yolo.keyborad.service.KeyboardProductItemsService; +/* +* @author: ziin +* @date: 2025/12/12 13:44 +*/ + +@Service +public class KeyboardProductItemsServiceImpl extends ServiceImpl implements KeyboardProductItemsService{ + + + /** + * 根据ID获取产品详情 + * + * @param id 产品ID + * @return 产品详情响应对象,如果ID为空或未找到产品则返回null + */ + @Override + public KeyboardProductItemRespVO getProductDetailById(Long id) { + // 参数校验:ID不能为空 + if (id == null) { + return null; + } + + // 根据ID查询产品信息 + KeyboardProductItems item = this.getById(id); + + // 将实体对象转换为响应VO对象并返回 + return item == null ? null : BeanUtil.copyProperties(item, KeyboardProductItemRespVO.class); + } + + + /** + * 根据产品ID获取产品详情 + * + * @param productId 产品ID + * @return 产品详情响应对象,如果产品ID为空或未找到产品则返回null + */ + @Override + public KeyboardProductItemRespVO getProductDetailByProductId(String productId) { + // 参数校验:产品ID不能为空 + if (productId == null || productId.isBlank()) { + return null; + } + + // 根据产品ID查询产品信息 + KeyboardProductItems item = this.lambdaQuery() + .eq(KeyboardProductItems::getProductId, productId) + .one(); + + // 将实体对象转换为响应VO对象并返回 + return item == null ? null : BeanUtil.copyProperties(item, KeyboardProductItemRespVO.class); + } + + + /** + * 根据类型获取产品列表 + * + * @param type 产品类型,如果为null、空字符串或"all"则查询所有产品 + * @return 产品详情响应列表,按ID升序排列 + */ + @Override + public java.util.List listProductsByType(String type) { + // 创建Lambda查询构造器 + var query = this.lambdaQuery(); + + // 如果类型参数有效且不是"all",则添加类型过滤条件 + if (type != null && !type.isBlank() && !"all".equalsIgnoreCase(type)) { + query.eq(KeyboardProductItems::getType, type); + } + + // 执行查询,按ID升序排列 + java.util.List items = query + .orderByAsc(KeyboardProductItems::getId) + .list(); + + // 将实体对象转换为响应VO对象并返回 + return items.stream() + .map(i -> BeanUtil.copyProperties(i, KeyboardProductItemRespVO.class)) + .toList(); + } +} diff --git a/src/main/resources/mapper/KeyboardProductItemsMapper.xml b/src/main/resources/mapper/KeyboardProductItemsMapper.xml new file mode 100644 index 0000000..288fb97 --- /dev/null +++ b/src/main/resources/mapper/KeyboardProductItemsMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + id, product_id, "type", "name", unit, duration_value, duration_unit, price, currency, + description, created_at, updated_at, duration_days + + \ No newline at end of file