JDWA 技术文档
首页
  • 数据库
  • 前端开发
  • 后端开发
  • 开发工具
  • 虚拟化技术
  • KVM显卡直通
  • FPGA仿真固件
  • 项目实战
  • 踩坑记录
  • 开发心得
  • 软件工具
  • 学习资料
  • 开发环境
更新日志
关于我
Gitee
GitHub
首页
  • 数据库
  • 前端开发
  • 后端开发
  • 开发工具
  • 虚拟化技术
  • KVM显卡直通
  • FPGA仿真固件
  • 项目实战
  • 踩坑记录
  • 开发心得
  • 软件工具
  • 学习资料
  • 开发环境
更新日志
关于我
Gitee
GitHub
  • Green-U低碳生活平台(后端)

    • JDWA Green-U 后端技术文档索引
    • JDWA Green-U 项目架构概述
    • JDWA Green-U 数据库设计文档
    • JDWA Green-U 用户认证模块技术文档
    • JDWA Green-U 用户活动模块技术文档
    • JDWA Green-U 碳减排统计模块技术文档
    • JDWA Green-U 积分系统模块技术文档
    • JDWA Green-U 成就系统模块技术文档
    • JDWA Green-U 奖品兑换模块技术文档
    • JDWA Green-U API接口文档
    • JDWA Green-U 系统安全机制与防护措施
    • JDWA Green-U 部署与运维指南

JDWA Green-U 奖品兑换模块

本文档详细介绍了JDWA Green-U项目中奖品兑换模块的设计思路、业务规则和核心实现。该模块负责奖品的管理、用户兑换流程、订单处理及物流集成,是用户环保行为获得实际回报的重要途径。

  • 模块概述
    • 业务价值
    • 功能范围
  • 核心功能设计
    • 奖品类型定义
    • 奖品等级设计
    • 兑换规则设计
    • 兑换流程设计
    • 订单状态管理
    • 物流集成方案
    • 奖品推荐系统
  • 数据模型设计
    • 核心表结构
    • 核心实体类
  • 核心类与接口
    • 控制器(JDWAPrizeController)
    • 服务接口(JDWAPrizeService)
    • 服务实现类(JDWAPrizeServiceImpl)
  • 性能优化
    • 高并发处理
    • 数据库优化
  • 常见问题与解决方案
    • 库存管理问题
    • 订单处理问题
    • 兑换资格验证问题
  • 未来优化规划
    • 短期规划(1-3个月)
    • 中期规划(3-6个月)
    • 长期规划(6-12个月)
  • 总结
  • API接口规范
    • 奖品管理接口
    • 购物车接口
    • 订单接口
  • 系统集成
    • 用户积分系统
    • 成就系统
    • 活动模块
    • 库存管理系统
    • 物流配送系统
    • 用户通知系统
  • 业务流程示例
    • 实物奖品兑换流程
    • 虚拟奖品兑换流程
  • 兑换规则引擎
    • 规则类型
    • 规则执行机制

模块概述

奖品兑换模块是JDWA Green-U平台激励体系的重要组成部分,将用户通过环保活动获得的积分和成就转化为实际可感知的奖励。通过丰富多样的奖品类型和便捷的兑换流程,提升用户参与环保活动的积极性,形成"行为-奖励-行为"的良性循环。

业务价值

  • 为用户提供环保行为的实际回报,增强用户获得感
  • 通过多样化的奖品选择,满足不同用户的需求和喜好
  • 提升用户参与平台环保活动的积极性和持续性
  • 丰富平台运营活动形式,支持特定主题的奖品兑换活动
  • 为合作伙伴提供生态友好产品的展示和推广渠道
  • 通过数据分析,了解用户偏好,指导产品和活动设计

功能范围

  • 奖品分类与管理
  • 积分兑换与兑换规则设置
  • 虚拟奖品与实物奖品处理
  • 兑换订单管理
  • 物流配送集成
  • 奖品推荐与个性化展示
  • 限时兑换与专题活动支持
  • 兑换数据统计与分析

核心功能设计

奖品类型定义

系统支持以下几种奖品类型:

奖品类型代码标识说明示例
实物奖品PHYSICAL需要物流配送的实体商品环保购物袋、不锈钢水杯、植物种子
虚拟商品VIRTUAL自动发放的电子商品电子书、音乐下载、视频课程
优惠券COUPON可在平台内或合作商家使用的折扣券商城折扣券、合作商家优惠券
会员特权PRIVILEGE平台特定功能的使用权限VIP功能、高级数据分析
公益捐赠DONATION将积分转化为公益捐赠植树、环保项目支持

奖品等级设计

根据价值和稀缺性,奖品分为以下等级:

  1. 普通奖品(COMMON):

    • 积分门槛低,库存充足
    • 兑换无特殊限制
    • 例如:环保袋、电子书券
  2. 精品奖品(PREMIUM):

    • 中等积分要求,限量供应
    • 可能有每人限兑数量
    • 例如:品牌环保水杯、优质植物种子套装
  3. 珍稀奖品(RARE):

    • 高积分要求,极为稀缺
    • 严格的兑换条件,如需同时满足积分和特定成就
    • 例如:限量版环保主题艺术品、高端环保科技产品
  4. 专属奖品(EXCLUSIVE):

    • 不对外公开,仅特定用户可见
    • 通常为定制化奖品或限时邀请
    • 例如:环保活动VIP体验、品牌联名定制品

兑换规则设计

  1. 积分兑换规则:

    • 基础规则:不同奖品设置不同的积分门槛
    • 会员等级影响:高级会员可享受积分优惠
    • 限时优惠:特定时段可降低积分要求
    • 组合支付:部分高价值奖品支持积分+现金组合支付
  2. 兑换限制:

    • 数量限制:单用户单个奖品的兑换上限
    • 时间限制:特定奖品的兑换开放时间段
    • 资格限制:需达到特定成就或会员等级
    • 区域限制:部分实物奖品可能有配送区域限制
  3. 库存管理:

    • 实时库存控制,避免超额兑换
    • 库存预警机制,及时补充热门奖品
    • 临时缺货时的候补登记功能
    • 部分奖品采用预约制,按批次生产和发放

兑换流程设计

  1. 虚拟奖品兑换流程:

    • 用户选择奖品 → 确认兑换信息
    • 系统校验资格 → 扣减积分
    • 生成兑换码/激活账号权限 → 发送通知
    • 兑换完成,记录订单
  2. 实物奖品兑换流程:

    • 用户选择奖品 → 填写收货信息
    • 系统校验资格与库存 → 扣减积分
    • 生成物流订单 → 等待发货
    • 物流配送 → 确认收货 → 兑换完成
  3. 公益捐赠流程:

    • 用户选择捐赠项目 → 确认捐赠金额
    • 系统扣减对应积分 → 生成捐赠证书
    • 定期执行实际捐赠 → 公示捐赠结果
    • 向用户反馈捐赠成果

订单状态管理

实物奖品订单状态流转:

  1. 待处理(PENDING):订单创建,等待系统确认
  2. 已确认(CONFIRMED):订单确认,等待仓库处理
  3. 备货中(PREPARING):仓库正在准备商品
  4. 已发货(SHIPPED):商品已发出,物流配送中
  5. 已签收(RECEIVED):用户已签收商品
  6. 已完成(COMPLETED):订单完成,可评价
  7. 已取消(CANCELLED):订单被取消
  8. 退换中(RETURNING):订单正在退换流程中
  9. 已退换(RETURNED):退换流程完成

虚拟奖品订单状态流转:

  1. 待处理(PENDING):订单创建,等待系统处理
  2. 处理中(PROCESSING):系统正在处理虚拟奖品发放
  3. 已发放(DELIVERED):虚拟奖品已发放到账
  4. 已使用(USED):虚拟奖品已被使用
  5. 已过期(EXPIRED):虚拟奖品已过期失效
  6. 已取消(CANCELLED):订单被取消

物流集成方案

系统支持多种物流配送方式:

  1. 第三方物流API集成:

    • 与主流快递公司API对接
    • 自动生成运单号和面单
    • 实时查询物流状态
    • 支持定时提醒和异常预警
  2. 自有配送体系(适用于部分城市):

    • 区域配送员派送
    • 实时配送状态更新
    • 用户与配送员直接沟通
  3. 门店自提点:

    • 合作商家作为自提点
    • 用户就近选择自提
    • 凭兑换码现场领取
    • 减少物流环节,更环保

奖品推荐系统

基于用户行为和偏好的个性化奖品推荐:

  1. 基础推荐维度:

    • 用户历史兑换记录
    • 用户环保活动偏好
    • 用户基本属性(年龄、性别、地区)
    • 当前积分余额
  2. 推荐策略:

    • 协同过滤:相似用户喜欢的奖品
    • 内容相似:与历史兑换奖品相似的新品
    • 积分匹配:符合用户当前积分范围的奖品
    • 热门榜单:当前平台热门兑换奖品

数据模型设计

核心表结构

奖品兑换模块主要涉及以下数据表:

  • jdwa_prize:奖品信息表
  • jdwa_prize_category:奖品分类表
  • jdwa_prize_stock:奖品库存表
  • jdwa_exchange_order:兑换订单表
  • jdwa_exchange_order_item:订单明细表
  • jdwa_logistics_info:物流信息表
  • jdwa_address:收货地址表
  • jdwa_prize_rule:兑换规则表

详细表结构如下:

CREATE TABLE `jdwa_prize` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '奖品ID',
  `prize_code` varchar(50) NOT NULL COMMENT '奖品编码',
  `prize_name` varchar(100) NOT NULL COMMENT '奖品名称',
  `prize_type` varchar(20) NOT NULL COMMENT '奖品类型:PHYSICAL/VIRTUAL/COUPON/PRIVILEGE/DONATION',
  `prize_level` varchar(20) NOT NULL DEFAULT 'COMMON' COMMENT '奖品等级:COMMON/PREMIUM/RARE/EXCLUSIVE',
  `category_id` bigint(20) NOT NULL COMMENT '分类ID',
  `points_price` int(11) NOT NULL COMMENT '积分价格',
  `market_price` decimal(10,2) DEFAULT NULL COMMENT '市场价格',
  `cash_price` decimal(10,2) DEFAULT NULL COMMENT '现金价格(组合支付时使用)',
  `thumbnail` varchar(255) DEFAULT NULL COMMENT '缩略图',
  `images` text COMMENT '详情图片(JSON数组)',
  `description` text COMMENT '奖品描述',
  `specification` text COMMENT '规格参数(JSON)',
  `exchange_limit` int(11) DEFAULT NULL COMMENT '每人兑换限制',
  `exchange_start` datetime DEFAULT NULL COMMENT '兑换开始时间',
  `exchange_end` datetime DEFAULT NULL COMMENT '兑换结束时间',
  `weight` decimal(10,2) DEFAULT NULL COMMENT '重量(kg)',
  `delivery_fee` decimal(10,2) DEFAULT '0.00' COMMENT '运费',
  `supplier` varchar(100) DEFAULT NULL COMMENT '供应商',
  `supplier_contact` varchar(100) DEFAULT NULL COMMENT '供应商联系方式',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-下架,1-上架',
  `is_hot` tinyint(4) DEFAULT '0' COMMENT '是否热门',
  `is_new` tinyint(4) DEFAULT '0' COMMENT '是否新品',
  `is_recommend` tinyint(4) DEFAULT '0' COMMENT '是否推荐',
  `sort_order` int(11) DEFAULT '0' COMMENT '排序顺序',
  `view_count` int(11) DEFAULT '0' COMMENT '浏览次数',
  `exchange_count` int(11) DEFAULT '0' COMMENT '已兑换次数',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_prize_code` (`prize_code`),
  KEY `idx_category_id` (`category_id`),
  KEY `idx_prize_level` (`prize_level`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='奖品信息表';

CREATE TABLE `jdwa_prize_category` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
  `category_name` varchar(50) NOT NULL COMMENT '分类名称',
  `parent_id` bigint(20) DEFAULT '0' COMMENT '父分类ID',
  `icon` varchar(255) DEFAULT NULL COMMENT '分类图标',
  `description` varchar(255) DEFAULT NULL COMMENT '分类描述',
  `sort_order` int(11) DEFAULT '0' COMMENT '排序顺序',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='奖品分类表';

CREATE TABLE `jdwa_prize_stock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '库存ID',
  `prize_id` bigint(20) NOT NULL COMMENT '奖品ID',
  `total_stock` int(11) NOT NULL DEFAULT '0' COMMENT '总库存',
  `available_stock` int(11) NOT NULL DEFAULT '0' COMMENT '可用库存',
  `locked_stock` int(11) NOT NULL DEFAULT '0' COMMENT '锁定库存',
  `warn_stock` int(11) DEFAULT '10' COMMENT '预警库存',
  `warehouse` varchar(50) DEFAULT NULL COMMENT '仓库编码',
  `last_restock_time` datetime DEFAULT NULL COMMENT '最后补货时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_prize_id` (`prize_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='奖品库存表';

CREATE TABLE `jdwa_exchange_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单编号',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `order_type` varchar(20) NOT NULL COMMENT '订单类型:PHYSICAL/VIRTUAL/MIXED',
  `total_points` int(11) NOT NULL COMMENT '总积分',
  `total_cash` decimal(10,2) DEFAULT '0.00' COMMENT '总现金(组合支付)',
  `total_amount` int(11) NOT NULL COMMENT '商品总数量',
  `status` varchar(20) NOT NULL COMMENT '订单状态',
  `payment_status` tinyint(4) DEFAULT '0' COMMENT '支付状态:0-未支付,1-已支付',
  `payment_time` datetime DEFAULT NULL COMMENT '支付时间',
  `delivery_method` varchar(20) DEFAULT NULL COMMENT '配送方式:EXPRESS/SELF_DELIVERY/PICKUP',
  `delivery_fee` decimal(10,2) DEFAULT '0.00' COMMENT '运费',
  `address_id` bigint(20) DEFAULT NULL COMMENT '收货地址ID',
  `consignee` varchar(50) DEFAULT NULL COMMENT '收货人',
  `consignee_phone` varchar(20) DEFAULT NULL COMMENT '收货人电话',
  `consignee_address` varchar(255) DEFAULT NULL COMMENT '收货地址',
  `logistics_company` varchar(50) DEFAULT NULL COMMENT '物流公司',
  `logistics_no` varchar(50) DEFAULT NULL COMMENT '物流单号',
  `delivery_time` datetime DEFAULT NULL COMMENT '发货时间',
  `receive_time` datetime DEFAULT NULL COMMENT '收货时间',
  `completion_time` datetime DEFAULT NULL COMMENT '完成时间',
  `cancel_reason` varchar(255) DEFAULT NULL COMMENT '取消原因',
  `cancel_time` datetime DEFAULT NULL COMMENT '取消时间',
  `remark` varchar(255) DEFAULT NULL COMMENT '订单备注',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_status` (`status`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='兑换订单表';

CREATE TABLE `jdwa_exchange_order_item` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID',
  `order_id` bigint(20) NOT NULL COMMENT '订单ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单编号',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `prize_id` bigint(20) NOT NULL COMMENT '奖品ID',
  `prize_code` varchar(50) NOT NULL COMMENT '奖品编码',
  `prize_name` varchar(100) NOT NULL COMMENT '奖品名称',
  `prize_type` varchar(20) NOT NULL COMMENT '奖品类型',
  `prize_image` varchar(255) DEFAULT NULL COMMENT '奖品图片',
  `points_price` int(11) NOT NULL COMMENT '积分价格',
  `cash_price` decimal(10,2) DEFAULT '0.00' COMMENT '现金价格',
  `quantity` int(11) NOT NULL DEFAULT '1' COMMENT '数量',
  `total_points` int(11) NOT NULL COMMENT '总积分',
  `total_cash` decimal(10,2) DEFAULT '0.00' COMMENT '总现金',
  `virtual_code` varchar(100) DEFAULT NULL COMMENT '虚拟商品兑换码',
  `virtual_info` text COMMENT '虚拟商品信息',
  `status` varchar(20) NOT NULL COMMENT '状态',
  `is_reviewed` tinyint(4) DEFAULT '0' COMMENT '是否已评价',
  `expire_time` datetime DEFAULT NULL COMMENT '过期时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_order_no` (`order_no`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_prize_id` (`prize_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

CREATE TABLE `jdwa_logistics_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '物流ID',
  `order_id` bigint(20) NOT NULL COMMENT '订单ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单编号',
  `logistics_company` varchar(50) NOT NULL COMMENT '物流公司',
  `logistics_code` varchar(20) DEFAULT NULL COMMENT '物流公司编码',
  `logistics_no` varchar(50) NOT NULL COMMENT '物流单号',
  `sender` varchar(50) DEFAULT NULL COMMENT '发件人',
  `sender_phone` varchar(20) DEFAULT NULL COMMENT '发件人电话',
  `sender_address` varchar(255) DEFAULT NULL COMMENT '发货地址',
  `consignee` varchar(50) NOT NULL COMMENT '收件人',
  `consignee_phone` varchar(20) NOT NULL COMMENT '收件人电话',
  `consignee_address` varchar(255) NOT NULL COMMENT '收货地址',
  `package_weight` decimal(10,2) DEFAULT NULL COMMENT '包裹重量(kg)',
  `package_quantity` int(11) DEFAULT '1' COMMENT '包裹数量',
  `status` varchar(20) DEFAULT NULL COMMENT '物流状态',
  `tracking_info` text COMMENT '物流跟踪信息(JSON)',
  `delivery_time` datetime DEFAULT NULL COMMENT '发货时间',
  `estimated_delivery` datetime DEFAULT NULL COMMENT '预计送达时间',
  `actual_delivery` datetime DEFAULT NULL COMMENT '实际送达时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_order_no` (`order_no`),
  KEY `idx_logistics_no` (`logistics_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流信息表';

CREATE TABLE `jdwa_address` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '地址ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `receiver` varchar(50) NOT NULL COMMENT '收货人',
  `phone` varchar(20) NOT NULL COMMENT '联系电话',
  `province` varchar(20) NOT NULL COMMENT '省份',
  `city` varchar(20) NOT NULL COMMENT '城市',
  `district` varchar(20) NOT NULL COMMENT '区/县',
  `detail_address` varchar(255) NOT NULL COMMENT '详细地址',
  `post_code` varchar(10) DEFAULT NULL COMMENT '邮政编码',
  `is_default` tinyint(4) DEFAULT '0' COMMENT '是否默认:0-否,1-是',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '是否删除:0-否,1-是',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收货地址表';

CREATE TABLE `jdwa_prize_rule` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '规则ID',
  `rule_code` varchar(50) NOT NULL COMMENT '规则编码',
  `rule_name` varchar(100) NOT NULL COMMENT '规则名称',
  `prize_id` bigint(20) DEFAULT NULL COMMENT '奖品ID(NULL表示全局规则)',
  `rule_type` varchar(20) NOT NULL COMMENT '规则类型:DISCOUNT/LIMIT/QUALIFICATION',
  `rule_condition` text NOT NULL COMMENT '规则条件(JSON)',
  `rule_value` varchar(255) NOT NULL COMMENT '规则值',
  `priority` int(11) DEFAULT '0' COMMENT '优先级',
  `start_time` datetime DEFAULT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `description` varchar(255) DEFAULT NULL COMMENT '规则描述',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_rule_code` (`rule_code`),
  KEY `idx_prize_id` (`prize_id`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='兑换规则表';

核心实体类

奖品实体 (JDWAPrize)

@Data
@TableName("jdwa_prize")
public class JDWAPrize {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String prizeCode;
    private String prizeName;
    private String prizeType;
    private String prizeLevel;
    private Long categoryId;
    private Integer pointsPrice;
    private BigDecimal marketPrice;
    private BigDecimal cashPrice;
    private String thumbnail;
    private String images;
    private String description;
    private String specification;
    private Integer exchangeLimit;
    private LocalDateTime exchangeStart;
    private LocalDateTime exchangeEnd;
    private BigDecimal weight;
    private BigDecimal deliveryFee;
    private String supplier;
    private String supplierContact;
    private Integer status;
    private Integer isHot;
    private Integer isNew;
    private Integer isRecommend;
    private Integer sortOrder;
    private Integer viewCount;
    private Integer exchangeCount;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    
    @TableField(exist = false)
    private JDWAPrizeCategory category;
    
    @TableField(exist = false)
    private JDWAPrizeStock stock;
    
    @TableField(exist = false)
    private List<JDWAPrizeRule> rules;
}

奖品分类实体 (JDWAPrizeCategory)

@Data
@TableName("jdwa_prize_category")
public class JDWAPrizeCategory {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String categoryName;
    private Long parentId;
    private String icon;
    private String description;
    private Integer sortOrder;
    private Integer status;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    
    @TableField(exist = false)
    private List<JDWAPrizeCategory> children;
}

奖品库存实体 (JDWAPrizeStock)

@Data
@TableName("jdwa_prize_stock")
public class JDWAPrizeStock {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long prizeId;
    private Integer totalStock;
    private Integer availableStock;
    private Integer lockedStock;
    private Integer warnStock;
    private String warehouse;
    private LocalDateTime lastRestockTime;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

兑换订单实体 (JDWAExchangeOrder)

@Data
@TableName("jdwa_exchange_order")
public class JDWAExchangeOrder {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo;
    private Long userId;
    private String orderType;
    private Integer totalPoints;
    private BigDecimal totalCash;
    private Integer totalAmount;
    private String status;
    private Integer paymentStatus;
    private LocalDateTime paymentTime;
    private String deliveryMethod;
    private BigDecimal deliveryFee;
    private Long addressId;
    private String consignee;
    private String consigneePhone;
    private String consigneeAddress;
    private String logisticsCompany;
    private String logisticsNo;
    private LocalDateTime deliveryTime;
    private LocalDateTime receiveTime;
    private LocalDateTime completionTime;
    private String cancelReason;
    private LocalDateTime cancelTime;
    private String remark;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    
    @TableField(exist = false)
    private List<JDWAExchangeOrderItem> items;
    
    @TableField(exist = false)
    private JDWALogisticsInfo logisticsInfo;
}

订单明细实体 (JDWAExchangeOrderItem)

@Data
@TableName("jdwa_exchange_order_item")
public class JDWAExchangeOrderItem {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long orderId;
    private String orderNo;
    private Long userId;
    private Long prizeId;
    private String prizeCode;
    private String prizeName;
    private String prizeType;
    private String prizeImage;
    private Integer pointsPrice;
    private BigDecimal cashPrice;
    private Integer quantity;
    private Integer totalPoints;
    private BigDecimal totalCash;
    private String virtualCode;
    private String virtualInfo;
    private String status;
    private Integer isReviewed;
    private LocalDateTime expireTime;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

物流信息实体 (JDWALogisticsInfo)

@Data
@TableName("jdwa_logistics_info")
public class JDWALogisticsInfo {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long orderId;
    private String orderNo;
    private String logisticsCompany;
    private String logisticsCode;
    private String logisticsNo;
    private String sender;
    private String senderPhone;
    private String senderAddress;
    private String consignee;
    private String consigneePhone;
    private String consigneeAddress;
    private BigDecimal packageWeight;
    private Integer packageQuantity;
    private String status;
    private String trackingInfo;
    private LocalDateTime deliveryTime;
    private LocalDateTime estimatedDelivery;
    private LocalDateTime actualDelivery;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

收货地址实体 (JDWAAddress)

@Data
@TableName("jdwa_address")
public class JDWAAddress {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private String receiver;
    private String phone;
    private String province;
    private String city;
    private String district;
    private String detailAddress;
    private String postCode;
    private Integer isDefault;
    private Integer isDeleted;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

兑换规则实体 (JDWAPrizeRule)

@Data
@TableName("jdwa_prize_rule")
public class JDWAPrizeRule {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String ruleCode;
    private String ruleName;
    private Long prizeId;
    private String ruleType;
    private String ruleCondition;
    private String ruleValue;
    private Integer priority;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private Integer status;
    private String description;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

常量定义

public class JDWAPrizeConstants {
    // 奖品类型
    public static final String PRIZE_TYPE_PHYSICAL = "PHYSICAL";  // 实物奖品
    public static final String PRIZE_TYPE_VIRTUAL = "VIRTUAL";    // 虚拟商品
    public static final String PRIZE_TYPE_COUPON = "COUPON";      // 优惠券
    public static final String PRIZE_TYPE_PRIVILEGE = "PRIVILEGE"; // 会员特权
    public static final String PRIZE_TYPE_DONATION = "DONATION";   // 公益捐赠
    
    // 奖品等级
    public static final String PRIZE_LEVEL_COMMON = "COMMON";       // 普通奖品
    public static final String PRIZE_LEVEL_PREMIUM = "PREMIUM";     // 精品奖品
    public static final String PRIZE_LEVEL_RARE = "RARE";           // 珍稀奖品
    public static final String PRIZE_LEVEL_EXCLUSIVE = "EXCLUSIVE"; // 专属奖品
    
    // 订单类型
    public static final String ORDER_TYPE_PHYSICAL = "PHYSICAL"; // 实物订单
    public static final String ORDER_TYPE_VIRTUAL = "VIRTUAL";   // 虚拟订单
    public static final String ORDER_TYPE_MIXED = "MIXED";       // 混合订单
    
    // 订单状态 - 实物
    public static final String ORDER_STATUS_PENDING = "PENDING";         // 待处理
    public static final String ORDER_STATUS_CONFIRMED = "CONFIRMED";     // 已确认
    public static final String ORDER_STATUS_PREPARING = "PREPARING";     // 备货中
    public static final String ORDER_STATUS_SHIPPED = "SHIPPED";         // 已发货
    public static final String ORDER_STATUS_RECEIVED = "RECEIVED";       // 已签收
    public static final String ORDER_STATUS_COMPLETED = "COMPLETED";     // 已完成
    public static final String ORDER_STATUS_CANCELLED = "CANCELLED";     // 已取消
    public static final String ORDER_STATUS_RETURNING = "RETURNING";     // 退换中
    public static final String ORDER_STATUS_RETURNED = "RETURNED";       // 已退换
    
    // 订单状态 - 虚拟
    public static final String VIRTUAL_STATUS_PENDING = "PENDING";       // 待处理
    public static final String VIRTUAL_STATUS_PROCESSING = "PROCESSING"; // 处理中
    public static final String VIRTUAL_STATUS_DELIVERED = "DELIVERED";   // 已发放
    public static final String VIRTUAL_STATUS_USED = "USED";             // 已使用
    public static final String VIRTUAL_STATUS_EXPIRED = "EXPIRED";       // 已过期
    public static final String VIRTUAL_STATUS_CANCELLED = "CANCELLED";   // 已取消
    
    // 配送方式
    public static final String DELIVERY_EXPRESS = "EXPRESS";           // 快递配送
    public static final String DELIVERY_SELF_DELIVERY = "SELF_DELIVERY"; // 自有配送
    public static final String DELIVERY_PICKUP = "PICKUP";             // 门店自提
    
    // 规则类型
    public static final String RULE_TYPE_DISCOUNT = "DISCOUNT";       // 积分优惠
    public static final String RULE_TYPE_LIMIT = "LIMIT";             // 兑换限制
    public static final String RULE_TYPE_QUALIFICATION = "QUALIFICATION"; // 资格限制
} 

核心类与接口

控制器(JDWAPrizeController)

@Slf4j
@RestController
@RequestMapping("/api/prizes")
public class JDWAPrizeController {

    @Autowired
    private JDWAPrizeService prizeService;
    
    /**
     * 获取奖品列表
     * @param categoryId 分类ID
     * @param prizeType 奖品类型
     * @param prizeLevel 奖品等级
     * @param keyword 关键词
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 奖品列表
     */
    @GetMapping("/list")
    public Result<PageResult<JDWAPrizeVO>> getPrizeList(
            @RequestParam(required = false) Long categoryId,
            @RequestParam(required = false) String prizeType,
            @RequestParam(required = false) String prizeLevel,
            @RequestParam(required = false) String keyword,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        
        log.info("Get prize list: categoryId={}, prizeType={}, prizeLevel={}, keyword={}", 
                categoryId, prizeType, prizeLevel, keyword);
        
        PageResult<JDWAPrizeVO> result = prizeService.getPrizeList(
                categoryId, prizeType, prizeLevel, keyword, pageNum, pageSize);
        
        return Result.success(result);
    }
    
    /**
     * 获取奖品详情
     * @param prizeCode 奖品编码
     * @return 奖品详情
     */
    @GetMapping("/detail/{prizeCode}")
    public Result<JDWAPrizeDetailVO> getPrizeDetail(@PathVariable String prizeCode) {
        log.info("Get prize detail: prizeCode={}", prizeCode);
        
        JDWAPrizeDetailVO detail = prizeService.getPrizeDetail(prizeCode);
        if (detail == null) {
            return Result.error(ErrorCode.PRIZE_NOT_FOUND, "奖品不存在");
        }
        
        return Result.success(detail);
    }
    
    /**
     * 获取奖品分类列表
     * @return 分类列表
     */
    @GetMapping("/categories")
    public Result<List<JDWAPrizeCategoryVO>> getCategories() {
        List<JDWAPrizeCategoryVO> categories = prizeService.getAllCategories();
        return Result.success(categories);
    }
    
    /**
     * 根据身份和条件推荐奖品
     * @param type 推荐类型:HOT/NEW/RECOMMEND/POINTS_MATCH
     * @param limit 限制数量
     * @return 推荐奖品列表
     */
    @GetMapping("/recommend")
    public Result<List<JDWAPrizeVO>> getRecommendPrizes(
            @RequestParam(defaultValue = "RECOMMEND") String type,
            @RequestParam(defaultValue = "4") Integer limit) {
        
        List<JDWAPrizeVO> prizes = prizeService.getRecommendPrizes(type, limit);
        return Result.success(prizes);
    }
    
    /**
     * 加入兑换购物车
     * @param request 加入购物车请求
     * @return 操作结果
     */
    @PostMapping("/cart/add")
    public Result<Void> addToCart(@RequestBody @Validated JDWACartAddRequest request) {
        log.info("Add to cart: {}", request);
        
        boolean success = prizeService.addToCart(request);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "加入购物车失败");
        }
        
        return Result.success();
    }
    
    /**
     * 获取购物车列表
     * @return 购物车列表
     */
    @GetMapping("/cart/list")
    public Result<List<JDWACartItemVO>> getCartList() {
        List<JDWACartItemVO> cartItems = prizeService.getCartItems();
        return Result.success(cartItems);
    }
    
    /**
     * 移除购物车商品
     * @param cartItemIds 购物车项ID列表
     * @return 操作结果
     */
    @PostMapping("/cart/remove")
    public Result<Void> removeFromCart(@RequestBody List<Long> cartItemIds) {
        log.info("Remove from cart: {}", cartItemIds);
        
        boolean success = prizeService.removeFromCart(cartItemIds);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "移除购物车商品失败");
        }
        
        return Result.success();
    }
    
    /**
     * 更新购物车数量
     * @param cartItemId 购物车项ID
     * @param quantity 数量
     * @return 操作结果
     */
    @PostMapping("/cart/update")
    public Result<Void> updateCartQuantity(
            @RequestParam Long cartItemId,
            @RequestParam Integer quantity) {
        
        log.info("Update cart quantity: cartItemId={}, quantity={}", cartItemId, quantity);
        
        boolean success = prizeService.updateCartQuantity(cartItemId, quantity);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "更新购物车数量失败");
        }
        
        return Result.success();
    }
    
    /**
     * 创建兑换订单
     * @param request 创建订单请求
     * @return 订单编号
     */
    @PostMapping("/order/create")
    public Result<String> createExchangeOrder(@RequestBody @Validated JDWAExchangeOrderCreateRequest request) {
        log.info("Create exchange order: {}", request);
        
        String orderNo = prizeService.createExchangeOrder(request);
        if (StringUtils.isEmpty(orderNo)) {
            return Result.error(ErrorCode.OPERATION_FAILED, "创建订单失败");
        }
        
        return Result.success(orderNo);
    }
    
    /**
     * 获取订单列表
     * @param status 订单状态
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 订单列表
     */
    @GetMapping("/order/list")
    public Result<PageResult<JDWAExchangeOrderVO>> getOrderList(
            @RequestParam(required = false) String status,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        
        PageResult<JDWAExchangeOrderVO> result = prizeService.getOrderList(status, pageNum, pageSize);
        return Result.success(result);
    }
    
    /**
     * 获取订单详情
     * @param orderNo 订单编号
     * @return 订单详情
     */
    @GetMapping("/order/detail/{orderNo}")
    public Result<JDWAExchangeOrderDetailVO> getOrderDetail(@PathVariable String orderNo) {
        JDWAExchangeOrderDetailVO detail = prizeService.getOrderDetail(orderNo);
        if (detail == null) {
            return Result.error(ErrorCode.ORDER_NOT_FOUND, "订单不存在");
        }
        
        return Result.success(detail);
    }
    
    /**
     * 取消订单
     * @param orderNo 订单编号
     * @param reason 取消原因
     * @return 操作结果
     */
    @PostMapping("/order/cancel")
    public Result<Void> cancelOrder(
            @RequestParam String orderNo,
            @RequestParam String reason) {
        
        log.info("Cancel order: orderNo={}, reason={}", orderNo, reason);
        
        boolean success = prizeService.cancelOrder(orderNo, reason);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "取消订单失败");
        }
        
        return Result.success();
    }
    
    /**
     * 确认收货
     * @param orderNo 订单编号
     * @return 操作结果
     */
    @PostMapping("/order/confirm")
    public Result<Void> confirmReceive(@RequestParam String orderNo) {
        log.info("Confirm receive: orderNo={}", orderNo);
        
        boolean success = prizeService.confirmReceive(orderNo);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "确认收货失败");
        }
        
        return Result.success();
    }
    
    /**
     * 获取订单物流信息
     * @param orderNo 订单编号
     * @return 物流信息
     */
    @GetMapping("/order/logistics/{orderNo}")
    public Result<JDWALogisticsInfoVO> getLogisticsInfo(@PathVariable String orderNo) {
        JDWALogisticsInfoVO logisticsInfo = prizeService.getLogisticsInfo(orderNo);
        if (logisticsInfo == null) {
            return Result.error(ErrorCode.LOGISTICS_NOT_FOUND, "物流信息不存在");
        }
        
        return Result.success(logisticsInfo);
    }
    
    /**
     * 激活虚拟奖品
     * @param orderItemId 订单项ID
     * @return 操作结果
     */
    @PostMapping("/virtual/activate")
    public Result<JDWAVirtualPrizeVO> activateVirtualPrize(@RequestParam Long orderItemId) {
        log.info("Activate virtual prize: orderItemId={}", orderItemId);
        
        JDWAVirtualPrizeVO result = prizeService.activateVirtualPrize(orderItemId);
        if (result == null) {
            return Result.error(ErrorCode.OPERATION_FAILED, "激活虚拟奖品失败");
        }
        
        return Result.success(result);
    }
    
    /**
     * 获取收货地址列表
     * @return 地址列表
     */
    @GetMapping("/address/list")
    public Result<List<JDWAAddressVO>> getAddressList() {
        List<JDWAAddressVO> addresses = prizeService.getUserAddresses();
        return Result.success(addresses);
    }
    
    /**
     * 添加新地址
     * @param address 地址信息
     * @return 操作结果
     */
    @PostMapping("/address/add")
    public Result<Long> addAddress(@RequestBody @Validated JDWAAddressVO address) {
        log.info("Add address: {}", address);
        
        Long addressId = prizeService.addAddress(address);
        if (addressId == null) {
            return Result.error(ErrorCode.OPERATION_FAILED, "添加地址失败");
        }
        
        return Result.success(addressId);
    }
    
    /**
     * 修改地址
     * @param address 地址信息
     * @return 操作结果
     */
    @PostMapping("/address/update")
    public Result<Void> updateAddress(@RequestBody @Validated JDWAAddressVO address) {
        log.info("Update address: {}", address);
        
        boolean success = prizeService.updateAddress(address);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "修改地址失败");
        }
        
        return Result.success();
    }
    
    /**
     * 删除地址
     * @param addressId 地址ID
     * @return 操作结果
     */
    @PostMapping("/address/delete")
    public Result<Void> deleteAddress(@RequestParam Long addressId) {
        log.info("Delete address: addressId={}", addressId);
        
        boolean success = prizeService.deleteAddress(addressId);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "删除地址失败");
        }
        
        return Result.success();
    }
    
    /**
     * 设置默认地址
     * @param addressId 地址ID
     * @return 操作结果
     */
    @PostMapping("/address/default")
    public Result<Void> setDefaultAddress(@RequestParam Long addressId) {
        log.info("Set default address: addressId={}", addressId);
        
        boolean success = prizeService.setDefaultAddress(addressId);
        if (!success) {
            return Result.error(ErrorCode.OPERATION_FAILED, "设置默认地址失败");
        }
        
        return Result.success();
    }
}

服务接口(JDWAPrizeService)

/**
 * 奖品兑换服务接口
 */
public interface JDWAPrizeService {

    /**
     * 获取奖品列表
     * @param categoryId 分类ID
     * @param prizeType 奖品类型
     * @param prizeLevel 奖品等级
     * @param keyword 关键词
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 奖品列表
     */
    PageResult<JDWAPrizeVO> getPrizeList(Long categoryId, String prizeType, 
                                       String prizeLevel, String keyword, 
                                       Integer pageNum, Integer pageSize);
    
    /**
     * 获取奖品详情
     * @param prizeCode 奖品编码
     * @return 奖品详情
     */
    JDWAPrizeDetailVO getPrizeDetail(String prizeCode);
    
    /**
     * 获取所有奖品分类
     * @return 分类列表
     */
    List<JDWAPrizeCategoryVO> getAllCategories();
    
    /**
     * 根据推荐类型获取推荐奖品
     * @param type 推荐类型
     * @param limit 限制数量
     * @return 推荐奖品列表
     */
    List<JDWAPrizeVO> getRecommendPrizes(String type, Integer limit);
    
    /**
     * 加入兑换购物车
     * @param request 加入购物车请求
     * @return 是否成功
     */
    boolean addToCart(JDWACartAddRequest request);
    
    /**
     * 获取购物车商品列表
     * @return 购物车商品列表
     */
    List<JDWACartItemVO> getCartItems();
    
    /**
     * 从购物车移除商品
     * @param cartItemIds 购物车项ID列表
     * @return 是否成功
     */
    boolean removeFromCart(List<Long> cartItemIds);
    
    /**
     * 更新购物车商品数量
     * @param cartItemId 购物车项ID
     * @param quantity 数量
     * @return 是否成功
     */
    boolean updateCartQuantity(Long cartItemId, Integer quantity);
    
    /**
     * 创建兑换订单
     * @param request 创建订单请求
     * @return 订单编号
     */
    String createExchangeOrder(JDWAExchangeOrderCreateRequest request);
    
    /**
     * 获取订单列表
     * @param status 订单状态
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 订单列表
     */
    PageResult<JDWAExchangeOrderVO> getOrderList(String status, Integer pageNum, Integer pageSize);
    
    /**
     * 获取订单详情
     * @param orderNo 订单编号
     * @return 订单详情
     */
    JDWAExchangeOrderDetailVO getOrderDetail(String orderNo);
    
    /**
     * 取消订单
     * @param orderNo 订单编号
     * @param reason 取消原因
     * @return 是否成功
     */
    boolean cancelOrder(String orderNo, String reason);
    
    /**
     * 确认收货
     * @param orderNo 订单编号
     * @return 是否成功
     */
    boolean confirmReceive(String orderNo);
    
    /**
     * 获取物流信息
     * @param orderNo 订单编号
     * @return 物流信息
     */
    JDWALogisticsInfoVO getLogisticsInfo(String orderNo);
    
    /**
     * 激活虚拟奖品
     * @param orderItemId 订单项ID
     * @return 虚拟奖品信息
     */
    JDWAVirtualPrizeVO activateVirtualPrize(Long orderItemId);
    
    /**
     * 获取用户收货地址列表
     * @return 地址列表
     */
    List<JDWAAddressVO> getUserAddresses();
    
    /**
     * 添加收货地址
     * @param address 地址信息
     * @return 地址ID
     */
    Long addAddress(JDWAAddressVO address);
    
    /**
     * 更新收货地址
     * @param address 地址信息
     * @return 是否成功
     */
    boolean updateAddress(JDWAAddressVO address);
    
    /**
     * 删除收货地址
     * @param addressId 地址ID
     * @return 是否成功
     */
    boolean deleteAddress(Long addressId);
    
    /**
     * 设置默认收货地址
     * @param addressId 地址ID
     * @return 是否成功
     */
    boolean setDefaultAddress(Long addressId);
    
    /**
     * 验证库存和兑换资格
     * @param prizeId 奖品ID
     * @param quantity 数量
     * @return 验证结果
     */
    JDWAExchangeVerifyResult verifyExchangeQualification(Long prizeId, Integer quantity);
    
    /**
     * 锁定奖品库存
     * @param prizeId 奖品ID
     * @param quantity 数量
     * @return 是否成功
     */
    boolean lockPrizeStock(Long prizeId, Integer quantity);
    
    /**
     * 释放奖品库存
     * @param prizeId 奖品ID
     * @param quantity 数量
     * @return 是否成功
     */
    boolean releasePrizeStock(Long prizeId, Integer quantity);
    
    /**
     * 扣减用户积分
     * @param userId 用户ID
     * @param points 积分数量
     * @param orderNo 订单号
     * @param description 描述
     * @return 是否成功
     */
    boolean deductUserPoints(Long userId, Integer points, String orderNo, String description);
}

服务实现类(JDWAPrizeServiceImpl)

@Slf4j
@Service
public class JDWAPrizeServiceImpl implements JDWAPrizeService {

    @Autowired
    private JDWAPrizeMapper prizeMapper;
    
    @Autowired
    private JDWAPrizeCategoryMapper categoryMapper;
    
    @Autowired
    private JDWAPrizeStockMapper stockMapper;
    
    @Autowired
    private JDWAPrizeRuleMapper ruleMapper;
    
    @Autowired
    private JDWACartItemMapper cartItemMapper;
    
    @Autowired
    private JDWAExchangeOrderMapper orderMapper;
    
    @Autowired
    private JDWAExchangeOrderItemMapper orderItemMapper;
    
    @Autowired
    private JDWALogisticsInfoMapper logisticsInfoMapper;
    
    @Autowired
    private JDWAAddressMapper addressMapper;
    
    @Autowired
    private JDWAUserPointsService userPointsService;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Value("${jdwa.prize.stock.lock.timeout:300}")
    private Long stockLockTimeout;
    
    @Value("${jdwa.prize.order.expire.minutes:30}")
    private Integer orderExpireMinutes;
    
    @Override
    public PageResult<JDWAPrizeVO> getPrizeList(Long categoryId, String prizeType, 
                                             String prizeLevel, String keyword, 
                                             Integer pageNum, Integer pageSize) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 构建查询条件
        LambdaQueryWrapper<JDWAPrize> wrapper = Wrappers.<JDWAPrize>lambdaQuery()
                .eq(JDWAPrize::getStatus, 1) // 只查询上架商品
                .eq(categoryId != null, JDWAPrize::getCategoryId, categoryId)
                .eq(StringUtils.isNotEmpty(prizeType), JDWAPrize::getPrizeType, prizeType)
                .eq(StringUtils.isNotEmpty(prizeLevel), JDWAPrize::getPrizeLevel, prizeLevel)
                .and(StringUtils.isNotEmpty(keyword), w -> w
                    .like(JDWAPrize::getPrizeName, keyword)
                    .or()
                    .like(JDWAPrize::getDescription, keyword)
                )
                .le(JDWAPrize::getExchangeStart, LocalDateTime.now())
                .ge(JDWAPrize::getExchangeEnd, LocalDateTime.now())
                .orderByDesc(JDWAPrize::getIsHot)
                .orderByDesc(JDWAPrize::getIsRecommend)
                .orderByDesc(JDWAPrize::getSortOrder);
        
        // 分页查询
        Page<JDWAPrize> page = new Page<>(pageNum, pageSize);
        Page<JDWAPrize> prizePage = prizeMapper.selectPage(page, wrapper);
        
        // 获取奖品对应库存
        List<JDWAPrize> prizeList = prizePage.getRecords();
        if (CollectionUtils.isEmpty(prizeList)) {
            return PageResult.empty();
        }
        
        // 获取库存信息
        List<Long> prizeIds = prizeList.stream()
                .map(JDWAPrize::getId)
                .collect(Collectors.toList());
        
        List<JDWAPrizeStock> stockList = stockMapper.selectList(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .in(JDWAPrizeStock::getPrizeId, prizeIds));
        
        Map<Long, JDWAPrizeStock> stockMap = stockList.stream()
                .collect(Collectors.toMap(JDWAPrizeStock::getPrizeId, stock -> stock));
        
        // 转换为VO对象
        List<JDWAPrizeVO> voList = prizeList.stream().map(prize -> {
            JDWAPrizeVO vo = new JDWAPrizeVO();
            BeanUtils.copyProperties(prize, vo);
            
            // 设置库存信息
            JDWAPrizeStock stock = stockMap.get(prize.getId());
            if (stock != null) {
                vo.setAvailableStock(stock.getAvailableStock());
            } else {
                vo.setAvailableStock(0);
            }
            
            // 检查用户是否有足够积分
            JDWAUserPoints userPoints = userPointsService.getUserPoints(userId);
            if (userPoints != null) {
                vo.setHasEnoughPoints(userPoints.getAvailablePoints() >= prize.getPointsPrice());
            } else {
                vo.setHasEnoughPoints(false);
            }
            
            return vo;
        }).collect(Collectors.toList());
        
        return new PageResult<>(voList, prizePage.getTotal(), pageNum, pageSize);
    }
    
    @Override
    public JDWAPrizeDetailVO getPrizeDetail(String prizeCode) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询奖品信息
        JDWAPrize prize = prizeMapper.selectOne(
                Wrappers.<JDWAPrize>lambdaQuery()
                        .eq(JDWAPrize::getPrizeCode, prizeCode)
                        .eq(JDWAPrize::getStatus, 1));
        
        if (prize == null) {
            return null;
        }
        
        // 增加浏览次数
        prizeMapper.increaseViewCount(prize.getId());
        
        // 查询库存信息
        JDWAPrizeStock stock = stockMapper.selectOne(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .eq(JDWAPrizeStock::getPrizeId, prize.getId()));
        
        // 查询分类信息
        JDWAPrizeCategory category = categoryMapper.selectById(prize.getCategoryId());
        
        // 查询规则信息
        List<JDWAPrizeRule> rules = ruleMapper.selectList(
                Wrappers.<JDWAPrizeRule>lambdaQuery()
                        .eq(JDWAPrizeRule::getPrizeId, prize.getId())
                        .eq(JDWAPrizeRule::getStatus, 1)
                        .le(JDWAPrizeRule::getStartTime, LocalDateTime.now())
                        .ge(JDWAPrizeRule::getEndTime, LocalDateTime.now())
                        .orderByDesc(JDWAPrizeRule::getPriority));
        
        // 查询用户积分
        JDWAUserPoints userPoints = userPointsService.getUserPoints(userId);
        
        // 转换为VO对象
        JDWAPrizeDetailVO detailVO = new JDWAPrizeDetailVO();
        BeanUtils.copyProperties(prize, detailVO);
        
        // 设置库存信息
        if (stock != null) {
            detailVO.setAvailableStock(stock.getAvailableStock());
            detailVO.setTotalStock(stock.getTotalStock());
        } else {
            detailVO.setAvailableStock(0);
            detailVO.setTotalStock(0);
        }
        
        // 设置分类信息
        if (category != null) {
            detailVO.setCategoryName(category.getCategoryName());
        }
        
        // 设置规则信息
        if (!CollectionUtils.isEmpty(rules)) {
            List<JDWAPrizeRuleVO> ruleVOs = rules.stream().map(rule -> {
                JDWAPrizeRuleVO ruleVO = new JDWAPrizeRuleVO();
                BeanUtils.copyProperties(rule, ruleVO);
                return ruleVO;
            }).collect(Collectors.toList());
            detailVO.setRules(ruleVOs);
        }
        
        // 设置积分信息
        if (userPoints != null) {
            detailVO.setUserCurrentPoints(userPoints.getAvailablePoints());
            detailVO.setHasEnoughPoints(userPoints.getAvailablePoints() >= prize.getPointsPrice());
        } else {
            detailVO.setUserCurrentPoints(0);
            detailVO.setHasEnoughPoints(false);
        }
        
        // 设置兑换资格
        detailVO.setCanExchange(verifyExchangeQualification(prize.getId(), 1).isQualified());
        
        return detailVO;
    }
    
    @Override
    public List<JDWAPrizeCategoryVO> getAllCategories() {
        // 查询所有启用的分类
        List<JDWAPrizeCategory> categories = categoryMapper.selectList(
                Wrappers.<JDWAPrizeCategory>lambdaQuery()
                        .eq(JDWAPrizeCategory::getStatus, 1)
                        .orderByAsc(JDWAPrizeCategory::getSortOrder));
        
        if (CollectionUtils.isEmpty(categories)) {
            return Collections.emptyList();
        }
        
        // 转换为树形结构
        Map<Long, List<JDWAPrizeCategory>> childrenMap = categories.stream()
                .filter(category -> !category.getParentId().equals(0L))
                .collect(Collectors.groupingBy(JDWAPrizeCategory::getParentId));
        
        // 转换为VO对象
        return categories.stream()
                .filter(category -> category.getParentId().equals(0L))
                .map(category -> {
                    JDWAPrizeCategoryVO vo = new JDWAPrizeCategoryVO();
                    BeanUtils.copyProperties(category, vo);
                    
                    // 设置子分类
                    List<JDWAPrizeCategory> children = childrenMap.get(category.getId());
                    if (!CollectionUtils.isEmpty(children)) {
                        List<JDWAPrizeCategoryVO> childrenVOs = children.stream().map(child -> {
                            JDWAPrizeCategoryVO childVO = new JDWAPrizeCategoryVO();
                            BeanUtils.copyProperties(child, childVO);
                            return childVO;
                        }).collect(Collectors.toList());
                        vo.setChildren(childrenVOs);
                    }
                    
                    return vo;
                }).collect(Collectors.toList());
    }
    
    @Override
    public List<JDWAPrizeVO> getRecommendPrizes(String type, Integer limit) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 构建查询条件
        LambdaQueryWrapper<JDWAPrize> wrapper = Wrappers.<JDWAPrize>lambdaQuery()
                .eq(JDWAPrize::getStatus, 1) // 只查询上架商品
                .le(JDWAPrize::getExchangeStart, LocalDateTime.now())
                .ge(JDWAPrize::getExchangeEnd, LocalDateTime.now());
        
        // 根据推荐类型筛选
        switch (type) {
            case "HOT":
                wrapper.eq(JDWAPrize::getIsHot, 1)
                       .orderByDesc(JDWAPrize::getExchangeCount);
                break;
            case "NEW":
                wrapper.eq(JDWAPrize::getIsNew, 1)
                       .orderByDesc(JDWAPrize::getCreateTime);
                break;
            case "RECOMMEND":
                wrapper.eq(JDWAPrize::getIsRecommend, 1)
                       .orderByDesc(JDWAPrize::getSortOrder);
                break;
            case "POINTS_MATCH":
                // 获取用户积分
                JDWAUserPoints userPoints = userPointsService.getUserPoints(userId);
                if (userPoints != null) {
                    Integer availablePoints = userPoints.getAvailablePoints();
                    // 积分匹配:查询积分价格小于等于用户可用积分的奖品
                    wrapper.le(JDWAPrize::getPointsPrice, availablePoints)
                           .orderByDesc(JDWAPrize::getPointsPrice);
                }
                break;
            default:
                wrapper.orderByDesc(JDWAPrize::getSortOrder);
        }
        
        // 限制查询数量
        Page<JDWAPrize> page = new Page<>(1, limit);
        Page<JDWAPrize> prizePage = prizeMapper.selectPage(page, wrapper);
        
        List<JDWAPrize> prizeList = prizePage.getRecords();
        if (CollectionUtils.isEmpty(prizeList)) {
            return Collections.emptyList();
        }
        
        // 获取库存信息
        List<Long> prizeIds = prizeList.stream()
                .map(JDWAPrize::getId)
                .collect(Collectors.toList());
        
        List<JDWAPrizeStock> stockList = stockMapper.selectList(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .in(JDWAPrizeStock::getPrizeId, prizeIds));
        
        Map<Long, JDWAPrizeStock> stockMap = stockList.stream()
                .collect(Collectors.toMap(JDWAPrizeStock::getPrizeId, stock -> stock));
        
        // 转换为VO对象
        return prizeList.stream().map(prize -> {
            JDWAPrizeVO vo = new JDWAPrizeVO();
            BeanUtils.copyProperties(prize, vo);
            
            // 设置库存信息
            JDWAPrizeStock stock = stockMap.get(prize.getId());
            if (stock != null) {
                vo.setAvailableStock(stock.getAvailableStock());
            } else {
                vo.setAvailableStock(0);
            }
            
            return vo;
        }).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addToCart(JDWACartAddRequest request) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询奖品信息
        JDWAPrize prize = prizeMapper.selectById(request.getPrizeId());
        if (prize == null || prize.getStatus() != 1) {
            log.error("Prize not found or not active: prizeId={}", request.getPrizeId());
            return false;
        }
        
        // 验证库存
        JDWAPrizeStock stock = stockMapper.selectOne(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .eq(JDWAPrizeStock::getPrizeId, request.getPrizeId()));
        
        if (stock == null || stock.getAvailableStock() < request.getQuantity()) {
            log.error("Insufficient stock: prizeId={}, required={}, available={}",
                    request.getPrizeId(), request.getQuantity(), 
                    stock != null ? stock.getAvailableStock() : 0);
            return false;
        }
        
        // 验证兑换资格
        JDWAExchangeVerifyResult verifyResult = verifyExchangeQualification(
                request.getPrizeId(), request.getQuantity());
        
        if (!verifyResult.isQualified()) {
            log.error("Exchange qualification check failed: prizeId={}, reason={}",
                    request.getPrizeId(), verifyResult.getMessage());
            return false;
        }
        
        // 检查购物车中是否已存在该商品
        JDWACartItem existingItem = cartItemMapper.selectOne(
                Wrappers.<JDWACartItem>lambdaQuery()
                        .eq(JDWACartItem::getUserId, userId)
                        .eq(JDWACartItem::getPrizeId, request.getPrizeId()));
        
        if (existingItem != null) {
            // 更新数量
            existingItem.setQuantity(existingItem.getQuantity() + request.getQuantity());
            existingItem.setUpdateTime(LocalDateTime.now());
            cartItemMapper.updateById(existingItem);
        } else {
            // 创建新购物车项
            JDWACartItem cartItem = new JDWACartItem();
            cartItem.setUserId(userId);
            cartItem.setPrizeId(request.getPrizeId());
            cartItem.setPrizeCode(prize.getPrizeCode());
            cartItem.setPrizeName(prize.getPrizeName());
            cartItem.setPrizeType(prize.getPrizeType());
            cartItem.setPrizeImage(prize.getThumbnail());
            cartItem.setPointsPrice(prize.getPointsPrice());
            cartItem.setCashPrice(prize.getCashPrice());
            cartItem.setQuantity(request.getQuantity());
            cartItem.setSelectedStatus(1);
            cartItem.setCreateTime(LocalDateTime.now());
            cartItem.setUpdateTime(LocalDateTime.now());
            
            cartItemMapper.insert(cartItem);
        }
        
        return true;
    }
    
    @Override
    public List<JDWACartItemVO> getCartItems() {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询购物车项
        List<JDWACartItem> cartItems = cartItemMapper.selectList(
                Wrappers.<JDWACartItem>lambdaQuery()
                        .eq(JDWACartItem::getUserId, userId)
                        .orderByDesc(JDWACartItem::getCreateTime));
        
        if (CollectionUtils.isEmpty(cartItems)) {
            return Collections.emptyList();
        }
        
        // 获取奖品ID列表
        List<Long> prizeIds = cartItems.stream()
                .map(JDWACartItem::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        
        // 查询奖品库存
        List<JDWAPrizeStock> stockList = stockMapper.selectList(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .in(JDWAPrizeStock::getPrizeId, prizeIds));
        
        Map<Long, JDWAPrizeStock> stockMap = stockList.stream()
                .collect(Collectors.toMap(JDWAPrizeStock::getPrizeId, stock -> stock));
        
        // 查询奖品当前信息
        List<JDWAPrize> prizeList = prizeMapper.selectList(
                Wrappers.<JDWAPrize>lambdaQuery()
                        .in(JDWAPrize::getId, prizeIds));
        
        Map<Long, JDWAPrize> prizeMap = prizeList.stream()
                .collect(Collectors.toMap(JDWAPrize::getId, prize -> prize));
        
        // 转换为VO对象
        return cartItems.stream().map(item -> {
            JDWACartItemVO vo = new JDWACartItemVO();
            BeanUtils.copyProperties(item, vo);
            
            // 更新奖品最新信息
            JDWAPrize prize = prizeMap.get(item.getPrizeId());
            if (prize != null) {
                vo.setPointsPrice(prize.getPointsPrice());
                vo.setCashPrice(prize.getCashPrice());
                vo.setPrizeStatus(prize.getStatus());
                
                // 判断是否可兑换(商品状态、时间)
                boolean canExchange = prize.getStatus() == 1 
                        && LocalDateTime.now().isAfter(prize.getExchangeStart())
                        && LocalDateTime.now().isBefore(prize.getExchangeEnd());
                vo.setCanExchange(canExchange);
            } else {
                vo.setPrizeStatus(0);
                vo.setCanExchange(false);
            }
            
            // 设置库存信息
            JDWAPrizeStock stock = stockMap.get(item.getPrizeId());
            if (stock != null) {
                vo.setAvailableStock(stock.getAvailableStock());
                vo.setStockEnough(stock.getAvailableStock() >= item.getQuantity());
            } else {
                vo.setAvailableStock(0);
                vo.setStockEnough(false);
            }
            
            // 计算总积分和总金额
            vo.setTotalPoints(vo.getPointsPrice() * item.getQuantity());
            vo.setTotalCash(vo.getCashPrice() != null ? 
                    vo.getCashPrice().multiply(new BigDecimal(item.getQuantity())) : BigDecimal.ZERO);
            
            return vo;
        }).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeFromCart(List<Long> cartItemIds) {
        if (CollectionUtils.isEmpty(cartItemIds)) {
            return true;
        }
        
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 删除购物车项
        int result = cartItemMapper.delete(
                Wrappers.<JDWACartItem>lambdaQuery()
                        .eq(JDWACartItem::getUserId, userId)
                        .in(JDWACartItem::getId, cartItemIds));
        
        return result > 0;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateCartQuantity(Long cartItemId, Integer quantity) {
        if (quantity <= 0) {
            return false;
        }
        
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询购物车项
        JDWACartItem cartItem = cartItemMapper.selectOne(
                Wrappers.<JDWACartItem>lambdaQuery()
                        .eq(JDWACartItem::getId, cartItemId)
                        .eq(JDWACartItem::getUserId, userId));
        
        if (cartItem == null) {
            return false;
        }
        
        // 验证库存
        JDWAPrizeStock stock = stockMapper.selectOne(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .eq(JDWAPrizeStock::getPrizeId, cartItem.getPrizeId()));
        
        if (stock == null || stock.getAvailableStock() < quantity) {
            log.error("Insufficient stock: prizeId={}, required={}, available={}",
                    cartItem.getPrizeId(), quantity, 
                    stock != null ? stock.getAvailableStock() : 0);
            return false;
        }
        
        // 更新数量
        cartItem.setQuantity(quantity);
        cartItem.setUpdateTime(LocalDateTime.now());
        cartItemMapper.updateById(cartItem);
        
        return true;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createExchangeOrder(JDWAExchangeOrderCreateRequest request) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 生成订单号
        String orderNo = generateOrderNo();
        
        // 处理订单商品
        List<JDWAExchangeOrderItemCreateDTO> items = request.getItems();
        if (CollectionUtils.isEmpty(items)) {
            log.error("Order items cannot be empty: userId={}", userId);
            return null;
        }
        
        // 检查库存和资格
        Map<Long, JDWAPrize> prizeMap = new HashMap<>();
        Map<Long, Integer> prizeQuantityMap = new HashMap<>();
        
        for (JDWAExchangeOrderItemCreateDTO item : items) {
            // 检查商品信息
            JDWAPrize prize = prizeMapper.selectById(item.getPrizeId());
            if (prize == null || prize.getStatus() != 1) {
                log.error("Prize not found or not active: prizeId={}", item.getPrizeId());
                return null;
            }
            
            prizeMap.put(prize.getId(), prize);
            
            // 累加同种商品数量
            prizeQuantityMap.merge(prize.getId(), item.getQuantity(), Integer::sum);
        }
        
        // 逐个检查库存和兑换资格
        for (Map.Entry<Long, Integer> entry : prizeQuantityMap.entrySet()) {
            Long prizeId = entry.getKey();
            Integer quantity = entry.getValue();
            
            // 检查库存
            JDWAPrizeStock stock = stockMapper.selectOne(
                    Wrappers.<JDWAPrizeStock>lambdaQuery()
                            .eq(JDWAPrizeStock::getPrizeId, prizeId));
            
            if (stock == null || stock.getAvailableStock() < quantity) {
                log.error("Insufficient stock: prizeId={}, required={}, available={}",
                        prizeId, quantity, stock != null ? stock.getAvailableStock() : 0);
                return null;
            }
            
            // 检查兑换资格
            JDWAExchangeVerifyResult verifyResult = verifyExchangeQualification(prizeId, quantity);
            if (!verifyResult.isQualified()) {
                log.error("Exchange qualification check failed: prizeId={}, reason={}",
                        prizeId, verifyResult.getMessage());
                return null;
            }
            
            // 锁定库存
            boolean locked = lockPrizeStock(prizeId, quantity);
            if (!locked) {
                log.error("Failed to lock stock: prizeId={}, quantity={}", prizeId, quantity);
                return null;
            }
        }
        
        try {
            // 创建订单
            JDWAExchangeOrder order = new JDWAExchangeOrder();
            order.setOrderNo(orderNo);
            order.setUserId(userId);
            
            // 确定订单类型
            boolean hasPhysical = false;
            boolean hasVirtual = false;
            
            // 计算总积分和总数量
            int totalPoints = 0;
            BigDecimal totalCash = BigDecimal.ZERO;
            int totalAmount = 0;
            
            // 创建订单项
            List<JDWAExchangeOrderItem> orderItems = new ArrayList<>();
            
            for (JDWAExchangeOrderItemCreateDTO itemDTO : items) {
                JDWAPrize prize = prizeMap.get(itemDTO.getPrizeId());
                
                // 判断商品类型
                if (JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(prize.getPrizeType())) {
                    hasPhysical = true;
                } else {
                    hasVirtual = true;
                }
                
                // 创建订单项
                JDWAExchangeOrderItem orderItem = new JDWAExchangeOrderItem();
                orderItem.setOrderId(null); // 稍后设置
                orderItem.setOrderNo(orderNo);
                orderItem.setUserId(userId);
                orderItem.setPrizeId(prize.getId());
                orderItem.setPrizeCode(prize.getPrizeCode());
                orderItem.setPrizeName(prize.getPrizeName());
                orderItem.setPrizeType(prize.getPrizeType());
                orderItem.setPrizeImage(prize.getThumbnail());
                orderItem.setPointsPrice(prize.getPointsPrice());
                orderItem.setCashPrice(prize.getCashPrice());
                orderItem.setQuantity(itemDTO.getQuantity());
                orderItem.setTotalPoints(prize.getPointsPrice() * itemDTO.getQuantity());
                
                if (prize.getCashPrice() != null) {
                    orderItem.setTotalCash(prize.getCashPrice()
                            .multiply(new BigDecimal(itemDTO.getQuantity())));
                } else {
                    orderItem.setTotalCash(BigDecimal.ZERO);
                }
                
                // 设置状态
                if (JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(prize.getPrizeType())) {
                    orderItem.setStatus(JDWAPrizeConstants.ORDER_STATUS_PENDING);
                } else {
                    orderItem.setStatus(JDWAPrizeConstants.VIRTUAL_STATUS_PENDING);
                }
                
                // 设置过期时间(虚拟奖品)
                if (!JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(prize.getPrizeType())) {
                    orderItem.setExpireTime(LocalDateTime.now().plusDays(90)); // 设置90天有效期
                }
                
                orderItem.setCreateTime(LocalDateTime.now());
                orderItem.setUpdateTime(LocalDateTime.now());
                
                orderItems.add(orderItem);
                
                // 累计总积分和总数量
                totalPoints += orderItem.getTotalPoints();
                totalCash = totalCash.add(orderItem.getTotalCash());
                totalAmount += itemDTO.getQuantity();
            }
            
            // 设置订单类型
            if (hasPhysical && hasVirtual) {
                order.setOrderType(JDWAPrizeConstants.ORDER_TYPE_MIXED);
            } else if (hasPhysical) {
                order.setOrderType(JDWAPrizeConstants.ORDER_TYPE_PHYSICAL);
            } else {
                order.setOrderType(JDWAPrizeConstants.ORDER_TYPE_VIRTUAL);
            }
            
            // 设置订单基本信息
            order.setTotalPoints(totalPoints);
            order.setTotalCash(totalCash);
            order.setTotalAmount(totalAmount);
            
            // 设置订单状态
            if (hasPhysical) {
                order.setStatus(JDWAPrizeConstants.ORDER_STATUS_PENDING);
                
                // 设置收货地址
                if (request.getAddressId() != null) {
                    JDWAAddress address = addressMapper.selectById(request.getAddressId());
                    if (address != null) {
                        order.setAddressId(address.getId());
                        order.setConsignee(address.getReceiver());
                        order.setConsigneePhone(address.getPhone());
                        order.setConsigneeAddress(address.getProvince() + address.getCity() + 
                                address.getDistrict() + address.getDetailAddress());
                    }
                }
                
                // 设置配送方式
                order.setDeliveryMethod(request.getDeliveryMethod());
                
                // 设置运费
                // TODO: 根据地址和配送方式计算运费
                order.setDeliveryFee(BigDecimal.ZERO);
            } else {
                order.setStatus(JDWAPrizeConstants.VIRTUAL_STATUS_PENDING);
            }
            
            // 设置支付状态
            order.setPaymentStatus(0); // 未支付
            
            // 设置备注
            order.setRemark(request.getRemark());
            
            order.setCreateTime(LocalDateTime.now());
            order.setUpdateTime(LocalDateTime.now());
            
            // 保存订单
            orderMapper.insert(order);
            
            // 设置订单项的订单ID并保存
            for (JDWAExchangeOrderItem orderItem : orderItems) {
                orderItem.setOrderId(order.getId());
                orderItemMapper.insert(orderItem);
            }
            
            // 扣减用户积分
            boolean deducted = deductUserPoints(userId, totalPoints, orderNo, "兑换奖品");
            if (!deducted) {
                log.error("Failed to deduct user points: userId={}, points={}", userId, totalPoints);
                throw new RuntimeException("Failed to deduct user points");
            }
            
            // 如果有现金部分,需要处理支付
            if (totalCash.compareTo(BigDecimal.ZERO) > 0) {
                // TODO: 处理现金支付
            } else {
                // 无需现金支付,直接更新支付状态
                order.setPaymentStatus(1);
                order.setPaymentTime(LocalDateTime.now());
                orderMapper.updateById(order);
            }
            
            // 处理虚拟奖品
            if (!hasPhysical) {
                // 虚拟奖品自动完成
                for (JDWAExchangeOrderItem orderItem : orderItems) {
                    if (!JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(orderItem.getPrizeType())) {
                        // 生成虚拟兑换码
                        String virtualCode = generateVirtualCode(orderItem.getPrizeId());
                        orderItem.setVirtualCode(virtualCode);
                        orderItem.setStatus(JDWAPrizeConstants.VIRTUAL_STATUS_DELIVERED);
                        orderItemMapper.updateById(orderItem);
                    }
                }
                
                // 更新订单状态
                order.setStatus(JDWAPrizeConstants.VIRTUAL_STATUS_DELIVERED);
                order.setCompletionTime(LocalDateTime.now());
                orderMapper.updateById(order);
            }
            
            // 从购物车移除已购商品
            if (!CollectionUtils.isEmpty(request.getCartItemIds())) {
                cartItemMapper.delete(
                        Wrappers.<JDWACartItem>lambdaQuery()
                                .eq(JDWACartItem::getUserId, userId)
                                .in(JDWACartItem::getId, request.getCartItemIds()));
            }
            
            // 设置订单过期时间
            redisTemplate.opsForValue().set(
                    String.format("order:expire:%s", orderNo),
                    orderNo,
                    orderExpireMinutes,
                    TimeUnit.MINUTES);
            
            return orderNo;
        } catch (Exception e) {
            log.error("Create order failed: userId={}, error={}", userId, e.getMessage(), e);
            
            // 释放锁定的库存
            for (Map.Entry<Long, Integer> entry : prizeQuantityMap.entrySet()) {
                releasePrizeStock(entry.getKey(), entry.getValue());
            }
            
            throw e;
        }
    }
    
    /**
     * 生成订单号
     */
    private String generateOrderNo() {
        return "JD" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())
                + RandomStringUtils.randomNumeric(6);
    }
    
    /**
     * 生成虚拟奖品兑换码
     */
    private String generateVirtualCode(Long prizeId) {
        return "VC" + prizeId + DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now())
                + RandomStringUtils.randomAlphanumeric(8).toUpperCase();
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean lockPrizeStock(Long prizeId, Integer quantity) {
        // 先用Redis分布式锁防止并发问题
        String lockKey = String.format("prize:stock:lock:%d", prizeId);
        Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        
        if (Boolean.FALSE.equals(locked)) {
            log.warn("Failed to acquire Redis lock for stock: prizeId={}", prizeId);
            return false;
        }
        
        try {
            // 查询当前库存
            JDWAPrizeStock stock = stockMapper.selectOne(
                    Wrappers.<JDWAPrizeStock>lambdaQuery()
                            .eq(JDWAPrizeStock::getPrizeId, prizeId));
            
            if (stock == null || stock.getAvailableStock() < quantity) {
                log.error("Insufficient stock when locking: prizeId={}, required={}, available={}",
                        prizeId, quantity, stock != null ? stock.getAvailableStock() : 0);
                return false;
            }
            
            // 更新库存
            int updated = stockMapper.lockStock(prizeId, quantity);
            return updated > 0;
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean releasePrizeStock(Long prizeId, Integer quantity) {
        // 先用Redis分布式锁防止并发问题
        String lockKey = String.format("prize:stock:lock:%d", prizeId);
        Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        
        if (Boolean.FALSE.equals(locked)) {
            log.warn("Failed to acquire Redis lock for releasing stock: prizeId={}", prizeId);
            return false;
        }
        
        try {
            // 查询当前库存
            JDWAPrizeStock stock = stockMapper.selectOne(
                    Wrappers.<JDWAPrizeStock>lambdaQuery()
                            .eq(JDWAPrizeStock::getPrizeId, prizeId));
            
            if (stock == null) {
                log.error("Stock not found when releasing: prizeId={}", prizeId);
                return false;
            }
            
            // 更新库存
            int updated = stockMapper.releaseStock(prizeId, quantity);
            return updated > 0;
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }
    
    @Override
    public JDWAExchangeVerifyResult verifyExchangeQualification(Long prizeId, Integer quantity) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询奖品信息
        JDWAPrize prize = prizeMapper.selectById(prizeId);
        if (prize == null || prize.getStatus() != 1) {
            return new JDWAExchangeVerifyResult(false, "奖品不存在或已下架");
        }
        
        // 检查兑换时间
        LocalDateTime now = LocalDateTime.now();
        if (prize.getExchangeStart() != null && now.isBefore(prize.getExchangeStart())) {
            return new JDWAExchangeVerifyResult(false, "兑换尚未开始");
        }
        
        if (prize.getExchangeEnd() != null && now.isAfter(prize.getExchangeEnd())) {
            return new JDWAExchangeVerifyResult(false, "兑换已结束");
        }
        
        // 检查库存
        JDWAPrizeStock stock = stockMapper.selectOne(
                Wrappers.<JDWAPrizeStock>lambdaQuery()
                        .eq(JDWAPrizeStock::getPrizeId, prizeId));
        
        if (stock == null || stock.getAvailableStock() < quantity) {
            return new JDWAExchangeVerifyResult(false, "库存不足");
        }
        
        // 检查用户积分
        JDWAUserPoints userPoints = userPointsService.getUserPoints(userId);
        if (userPoints == null || userPoints.getAvailablePoints() < prize.getPointsPrice() * quantity) {
            return new JDWAExchangeVerifyResult(false, "积分不足");
        }
        
        // 检查每人兑换限制
        if (prize.getExchangeLimit() != null && prize.getExchangeLimit() > 0) {
            // 查询用户已兑换数量
            Integer exchangedCount = orderItemMapper.selectUserExchangedCount(userId, prizeId);
            if (exchangedCount != null && exchangedCount + quantity > prize.getExchangeLimit()) {
                return new JDWAExchangeVerifyResult(false, 
                        String.format("超出每人限兑%d个的限制", prize.getExchangeLimit()));
            }
        }
        
        // 检查兑换规则
        List<JDWAPrizeRule> rules = ruleMapper.selectList(
                Wrappers.<JDWAPrizeRule>lambdaQuery()
                        .eq(JDWAPrizeRule::getPrizeId, prizeId)
                        .eq(JDWAPrizeRule::getStatus, 1)
                        .le(JDWAPrizeRule::getStartTime, now)
                        .ge(JDWAPrizeRule::getEndTime, now)
                        .orderByDesc(JDWAPrizeRule::getPriority));
        
        if (!CollectionUtils.isEmpty(rules)) {
            // 检查资格限制类规则
            for (JDWAPrizeRule rule : rules) {
                if (JDWAPrizeConstants.RULE_TYPE_QUALIFICATION.equals(rule.getRuleType())) {
                    // 解析规则条件
                    try {
                        QualificationRule qualificationRule = 
                                JsonUtils.parseObject(rule.getRuleCondition(), QualificationRule.class);
                        
                        if (qualificationRule != null) {
                            // 根据规则类型进行检查
                            switch (qualificationRule.getQualificationType()) {
                                case "USER_LEVEL":
                                    // 检查用户等级
                                    JDWAUser user = userService.getUserById(userId);
                                    if (user == null || user.getUserLevel() < qualificationRule.getMinLevel()) {
                                        return new JDWAExchangeVerifyResult(false, 
                                                String.format("需要达到%d级才能兑换", qualificationRule.getMinLevel()));
                                    }
                                    break;
                                case "ACTIVITY_COUNT":
                                    // 检查活动参与次数
                                    Integer activityCount = 
                                            activityService.getUserActivityCount(userId, qualificationRule.getActivityType());
                                    if (activityCount < qualificationRule.getMinCount()) {
                                        return new JDWAExchangeVerifyResult(false, 
                                                String.format("需要参与%d次%s活动才能兑换", 
                                                        qualificationRule.getMinCount(), 
                                                        qualificationRule.getActivityTypeName()));
                                    }
                                    break;
                                case "ACHIEVEMENT":
                                    // 检查成就解锁
                                    boolean hasAchievement = 
                                            achievementService.hasAchievement(userId, qualificationRule.getAchievementCode());
                                    if (!hasAchievement) {
                                        return new JDWAExchangeVerifyResult(false, 
                                                String.format("需要解锁%s成就才能兑换", qualificationRule.getAchievementName()));
                                    }
                                    break;
                                default:
                                    log.warn("Unknown qualification type: {}", qualificationRule.getQualificationType());
                            }
                        }
                    } catch (Exception e) {
                        log.error("Failed to parse qualification rule: ruleId={}, error={}",
                                rule.getId(), e.getMessage(), e);
                    }
                }
            }
        }
        
        return new JDWAExchangeVerifyResult(true, "验证通过");
    }
    
    @Override
    public PageResult<JDWAExchangeOrderVO> getOrderList(String status, Integer pageNum, Integer pageSize) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 构建查询条件
        LambdaQueryWrapper<JDWAExchangeOrder> wrapper = Wrappers.<JDWAExchangeOrder>lambdaQuery()
                .eq(JDWAExchangeOrder::getUserId, userId)
                .eq(StringUtils.isNotEmpty(status), JDWAExchangeOrder::getStatus, status)
                .orderByDesc(JDWAExchangeOrder::getCreateTime);
        
        // 分页查询
        Page<JDWAExchangeOrder> page = new Page<>(pageNum, pageSize);
        Page<JDWAExchangeOrder> orderPage = orderMapper.selectPage(page, wrapper);
        
        List<JDWAExchangeOrder> orderList = orderPage.getRecords();
        if (CollectionUtils.isEmpty(orderList)) {
            return PageResult.empty();
        }
        
        // 获取订单ID列表
        List<Long> orderIds = orderList.stream()
                .map(JDWAExchangeOrder::getId)
                .collect(Collectors.toList());
        
        // 查询订单项
        List<JDWAExchangeOrderItem> itemList = orderItemMapper.selectList(
                Wrappers.<JDWAExchangeOrderItem>lambdaQuery()
                        .in(JDWAExchangeOrderItem::getOrderId, orderIds));
        
        // 按订单ID分组
        Map<Long, List<JDWAExchangeOrderItem>> itemMap = itemList.stream()
                .collect(Collectors.groupingBy(JDWAExchangeOrderItem::getOrderId));
        
        // 转换为VO对象
        List<JDWAExchangeOrderVO> voList = orderList.stream().map(order -> {
            JDWAExchangeOrderVO vo = new JDWAExchangeOrderVO();
            BeanUtils.copyProperties(order, vo);
            
            // 设置订单项信息
            List<JDWAExchangeOrderItem> items = itemMap.get(order.getId());
            if (!CollectionUtils.isEmpty(items)) {
                List<JDWAExchangeOrderItemVO> itemVOs = items.stream().map(item -> {
                    JDWAExchangeOrderItemVO itemVO = new JDWAExchangeOrderItemVO();
                    BeanUtils.copyProperties(item, itemVO);
                    return itemVO;
                }).collect(Collectors.toList());
                vo.setItems(itemVOs);
                
                // 设置首个商品图片作为订单展示图
                vo.setFirstPrizeImage(items.get(0).getPrizeImage());
            }
            
            return vo;
        }).collect(Collectors.toList());
        
        return new PageResult<>(voList, orderPage.getTotal(), pageNum, pageSize);
    }
    
    @Override
    public JDWAExchangeOrderDetailVO getOrderDetail(String orderNo) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询订单信息
        JDWAExchangeOrder order = orderMapper.selectOne(
                Wrappers.<JDWAExchangeOrder>lambdaQuery()
                        .eq(JDWAExchangeOrder::getOrderNo, orderNo)
                        .eq(JDWAExchangeOrder::getUserId, userId));
        
        if (order == null) {
            return null;
        }
        
        // 查询订单项
        List<JDWAExchangeOrderItem> items = orderItemMapper.selectList(
                Wrappers.<JDWAExchangeOrderItem>lambdaQuery()
                        .eq(JDWAExchangeOrderItem::getOrderId, order.getId()));
        
        // 查询物流信息
        JDWALogisticsInfo logistics = null;
        if (JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(order.getOrderType())
                || JDWAPrizeConstants.PRIZE_TYPE_MIXED.equals(order.getOrderType())) {
            logistics = logisticsInfoMapper.selectOne(
                    Wrappers.<JDWALogisticsInfo>lambdaQuery()
                            .eq(JDWALogisticsInfo::getOrderId, order.getId()));
        }
        
        // 转换为VO对象
        JDWAExchangeOrderDetailVO detailVO = new JDWAExchangeOrderDetailVO();
        BeanUtils.copyProperties(order, detailVO);
        
        // 设置订单项信息
        if (!CollectionUtils.isEmpty(items)) {
            List<JDWAExchangeOrderItemVO> itemVOs = items.stream().map(item -> {
                JDWAExchangeOrderItemVO itemVO = new JDWAExchangeOrderItemVO();
                BeanUtils.copyProperties(item, itemVO);
                return itemVO;
            }).collect(Collectors.toList());
            detailVO.setItems(itemVOs);
        }
        
        // 设置物流信息
        if (logistics != null) {
            JDWALogisticsInfoVO logisticsVO = new JDWALogisticsInfoVO();
            BeanUtils.copyProperties(logistics, logisticsVO);
            detailVO.setLogistics(logisticsVO);
        }
        
        return detailVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelOrder(String orderNo, String reason) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询订单信息
        JDWAExchangeOrder order = orderMapper.selectOne(
                Wrappers.<JDWAExchangeOrder>lambdaQuery()
                        .eq(JDWAExchangeOrder::getOrderNo, orderNo)
                        .eq(JDWAExchangeOrder::getUserId, userId));
        
        if (order == null) {
            log.error("Order not found for cancellation: orderNo={}", orderNo);
            return false;
        }
        
        // 检查订单状态
        if (JDWAPrizeConstants.ORDER_TYPE_PHYSICAL.equals(order.getOrderType())
                || JDWAPrizeConstants.ORDER_TYPE_MIXED.equals(order.getOrderType())) {
            // 实物订单只有待处理和已确认状态可以取消
            if (!JDWAPrizeConstants.ORDER_STATUS_PENDING.equals(order.getStatus())
                    && !JDWAPrizeConstants.ORDER_STATUS_CONFIRMED.equals(order.getStatus())) {
                log.error("Cannot cancel order with status: orderNo={}, status={}", 
                        orderNo, order.getStatus());
                return false;
            }
        } else {
            // 虚拟订单只有待处理和处理中状态可以取消
            if (!JDWAPrizeConstants.VIRTUAL_STATUS_PENDING.equals(order.getStatus())
                    && !JDWAPrizeConstants.VIRTUAL_STATUS_PROCESSING.equals(order.getStatus())) {
                log.error("Cannot cancel virtual order with status: orderNo={}, status={}", 
                        orderNo, order.getStatus());
                return false;
            }
        }
        
        // 查询订单项
        List<JDWAExchangeOrderItem> items = orderItemMapper.selectList(
                Wrappers.<JDWAExchangeOrderItem>lambdaQuery()
                        .eq(JDWAExchangeOrderItem::getOrderId, order.getId()));
        
        if (CollectionUtils.isEmpty(items)) {
            log.error("No order items found: orderNo={}", orderNo);
            return false;
        }
        
        // 更新订单状态
        order.setStatus(JDWAPrizeConstants.ORDER_STATUS_CANCELLED);
        order.setCancelReason(reason);
        order.setCancelTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        
        orderMapper.updateById(order);
        
        // 更新订单项状态
        for (JDWAExchangeOrderItem item : items) {
            if (JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(item.getPrizeType())) {
                item.setStatus(JDWAPrizeConstants.ORDER_STATUS_CANCELLED);
            } else {
                item.setStatus(JDWAPrizeConstants.VIRTUAL_STATUS_CANCELLED);
            }
            
            item.setUpdateTime(LocalDateTime.now());
            orderItemMapper.updateById(item);
            
            // 释放库存
            releasePrizeStock(item.getPrizeId(), item.getQuantity());
        }
        
        // 退还积分
        if (order.getTotalPoints() > 0) {
            userPointsService.refundPoints(
                    userId, order.getTotalPoints(), order.getOrderNo(), "取消订单退还积分");
        }
        
        return true;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean confirmReceive(String orderNo) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询订单信息
        JDWAExchangeOrder order = orderMapper.selectOne(
                Wrappers.<JDWAExchangeOrder>lambdaQuery()
                        .eq(JDWAExchangeOrder::getOrderNo, orderNo)
                        .eq(JDWAExchangeOrder::getUserId, userId));
        
        if (order == null) {
            log.error("Order not found for confirmation: orderNo={}", orderNo);
            return false;
        }
        
        // 检查订单状态,只有已发货状态可以确认收货
        if (!JDWAPrizeConstants.ORDER_STATUS_SHIPPED.equals(order.getStatus())) {
            log.error("Cannot confirm receipt for order with status: orderNo={}, status={}", 
                    orderNo, order.getStatus());
            return false;
        }
        
        // 更新订单状态
        order.setStatus(JDWAPrizeConstants.ORDER_STATUS_RECEIVED);
        order.setReceiveTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        
        // 设置完成时间
        order.setCompletionTime(LocalDateTime.now());
        order.setStatus(JDWAPrizeConstants.ORDER_STATUS_COMPLETED);
        
        orderMapper.updateById(order);
        
        // 更新订单项状态
        List<JDWAExchangeOrderItem> items = orderItemMapper.selectList(
                Wrappers.<JDWAExchangeOrderItem>lambdaQuery()
                        .eq(JDWAExchangeOrderItem::getOrderId, order.getId()));
        
        if (!CollectionUtils.isEmpty(items)) {
            for (JDWAExchangeOrderItem item : items) {
                if (JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(item.getPrizeType())) {
                    item.setStatus(JDWAPrizeConstants.ORDER_STATUS_COMPLETED);
                }
                
                item.setUpdateTime(LocalDateTime.now());
                orderItemMapper.updateById(item);
            }
        }
        
        // 更新物流信息
        JDWALogisticsInfo logistics = logisticsInfoMapper.selectOne(
                Wrappers.<JDWALogisticsInfo>lambdaQuery()
                        .eq(JDWALogisticsInfo::getOrderId, order.getId()));
        
        if (logistics != null) {
            logistics.setStatus("DELIVERED");
            logistics.setActualDelivery(LocalDateTime.now());
            logistics.setUpdateTime(LocalDateTime.now());
            logisticsInfoMapper.updateById(logistics);
        }
        
        return true;
    }
    
    @Override
    public JDWALogisticsInfoVO getLogisticsInfo(String orderNo) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询订单信息
        JDWAExchangeOrder order = orderMapper.selectOne(
                Wrappers.<JDWAExchangeOrder>lambdaQuery()
                        .eq(JDWAExchangeOrder::getOrderNo, orderNo)
                        .eq(JDWAExchangeOrder::getUserId, userId));
        
        if (order == null) {
            log.error("Order not found for logistics query: orderNo={}", orderNo);
            return null;
        }
        
        // 检查订单类型,只有实物或混合订单才有物流信息
        if (!JDWAPrizeConstants.ORDER_TYPE_PHYSICAL.equals(order.getOrderType())
                && !JDWAPrizeConstants.ORDER_TYPE_MIXED.equals(order.getOrderType())) {
            log.error("Not a physical order: orderNo={}, type={}", orderNo, order.getOrderType());
            return null;
        }
        
        // 查询物流信息
        JDWALogisticsInfo logistics = logisticsInfoMapper.selectOne(
                Wrappers.<JDWALogisticsInfo>lambdaQuery()
                        .eq(JDWALogisticsInfo::getOrderId, order.getId()));
        
        if (logistics == null) {
            return null;
        }
        
        // 转换为VO对象
        JDWALogisticsInfoVO logisticsVO = new JDWALogisticsInfoVO();
        BeanUtils.copyProperties(logistics, logisticsVO);
        
        // 补充订单信息
        logisticsVO.setOrderNo(order.getOrderNo());
        logisticsVO.setOrderStatus(order.getStatus());
        
        // 解析跟踪信息
        if (StringUtils.isNotEmpty(logistics.getTrackingInfo())) {
            try {
                List<JDWALogisticsTrackingVO> trackingList = 
                        JsonUtils.parseArray(logistics.getTrackingInfo(), JDWALogisticsTrackingVO.class);
                logisticsVO.setTrackingList(trackingList);
            } catch (Exception e) {
                log.error("Failed to parse tracking info: logisticsId={}, error={}", 
                        logistics.getId(), e.getMessage(), e);
            }
        }
        
        return logisticsVO;
    }
    
    @Override
    public JDWAVirtualPrizeVO activateVirtualPrize(Long orderItemId) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询订单项
        JDWAExchangeOrderItem orderItem = orderItemMapper.selectOne(
                Wrappers.<JDWAExchangeOrderItem>lambdaQuery()
                        .eq(JDWAExchangeOrderItem::getId, orderItemId)
                        .eq(JDWAExchangeOrderItem::getUserId, userId));
        
        if (orderItem == null) {
            log.error("Order item not found for activation: orderItemId={}", orderItemId);
            return null;
        }
        
        // 检查订单项类型和状态
        if (JDWAPrizeConstants.PRIZE_TYPE_PHYSICAL.equals(orderItem.getPrizeType())) {
            log.error("Cannot activate physical prize: orderItemId={}", orderItemId);
            return null;
        }
        
        if (!JDWAPrizeConstants.VIRTUAL_STATUS_DELIVERED.equals(orderItem.getStatus())) {
            log.error("Cannot activate prize with status: orderItemId={}, status={}", 
                    orderItemId, orderItem.getStatus());
            return null;
        }
        
        // 检查是否已过期
        if (orderItem.getExpireTime() != null && LocalDateTime.now().isAfter(orderItem.getExpireTime())) {
            log.error("Virtual prize has expired: orderItemId={}, expireTime={}", 
                    orderItemId, orderItem.getExpireTime());
            return null;
        }
        
        // 更新状态为已使用
        orderItem.setStatus(JDWAPrizeConstants.VIRTUAL_STATUS_USED);
        orderItem.setUpdateTime(LocalDateTime.now());
        orderItemMapper.updateById(orderItem);
        
        // 构建虚拟奖品信息
        JDWAVirtualPrizeVO vo = new JDWAVirtualPrizeVO();
        vo.setOrderItemId(orderItem.getId());
        vo.setOrderNo(orderItem.getOrderNo());
        vo.setPrizeId(orderItem.getPrizeId());
        vo.setPrizeCode(orderItem.getPrizeCode());
        vo.setPrizeName(orderItem.getPrizeName());
        vo.setPrizeType(orderItem.getPrizeType());
        vo.setVirtualCode(orderItem.getVirtualCode());
        
        // 解析虚拟商品信息
        if (StringUtils.isNotEmpty(orderItem.getVirtualInfo())) {
            try {
                Map<String, Object> virtualInfo = 
                        JsonUtils.parseObject(orderItem.getVirtualInfo(), new TypeReference<Map<String, Object>>() {});
                vo.setVirtualInfo(virtualInfo);
            } catch (Exception e) {
                log.error("Failed to parse virtual info: orderItemId={}, error={}", 
                        orderItemId, e.getMessage(), e);
            }
        }
        
        vo.setExpireTime(orderItem.getExpireTime());
        vo.setStatus(orderItem.getStatus());
        
        return vo;
    }
    
    @Override
    public List<JDWAAddressVO> getUserAddresses() {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询用户地址
        List<JDWAAddress> addresses = addressMapper.selectList(
                Wrappers.<JDWAAddress>lambdaQuery()
                        .eq(JDWAAddress::getUserId, userId)
                        .eq(JDWAAddress::getIsDeleted, 0)
                        .orderByDesc(JDWAAddress::getIsDefault)
                        .orderByDesc(JDWAAddress::getUpdateTime));
        
        if (CollectionUtils.isEmpty(addresses)) {
            return Collections.emptyList();
        }
        
        // 转换为VO对象
        return addresses.stream().map(address -> {
            JDWAAddressVO vo = new JDWAAddressVO();
            BeanUtils.copyProperties(address, vo);
            return vo;
        }).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addAddress(JDWAAddressVO addressVO) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 检查用户地址数量
        Integer addressCount = addressMapper.selectCount(
                Wrappers.<JDWAAddress>lambdaQuery()
                        .eq(JDWAAddress::getUserId, userId)
                        .eq(JDWAAddress::getIsDeleted, 0));
        
        if (addressCount >= 20) {
            log.error("Too many addresses: userId={}, count={}", userId, addressCount);
            return null;
        }
        
        // 创建地址
        JDWAAddress address = new JDWAAddress();
        BeanUtils.copyProperties(addressVO, address);
        address.setUserId(userId);
        
        // 如果是第一个地址,设为默认
        if (addressCount == 0) {
            address.setIsDefault(1);
        } else if (addressVO.getIsDefault() != null && addressVO.getIsDefault() == 1) {
            // 如果设为默认,先取消其他默认地址
            addressMapper.update(null, 
                    Wrappers.<JDWAAddress>lambdaUpdate()
                            .eq(JDWAAddress::getUserId, userId)
                            .eq(JDWAAddress::getIsDefault, 1)
                            .set(JDWAAddress::getIsDefault, 0));
        }
        
        address.setCreateTime(LocalDateTime.now());
        address.setUpdateTime(LocalDateTime.now());
        
        addressMapper.insert(address);
        
        return address.getId();
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAddress(JDWAAddressVO addressVO) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询地址是否存在
        JDWAAddress existingAddress = addressMapper.selectOne(
                Wrappers.<JDWAAddress>lambdaQuery()
                        .eq(JDWAAddress::getId, addressVO.getId())
                        .eq(JDWAAddress::getUserId, userId)
                        .eq(JDWAAddress::getIsDeleted, 0));
        
        if (existingAddress == null) {
            log.error("Address not found for update: addressId={}", addressVO.getId());
            return false;
        }
        
        // 更新地址
        JDWAAddress address = new JDWAAddress();
        BeanUtils.copyProperties(addressVO, address);
        address.setUserId(userId);
        address.setUpdateTime(LocalDateTime.now());
        
        // 如果设为默认,先取消其他默认地址
        if (addressVO.getIsDefault() != null && addressVO.getIsDefault() == 1 
                && existingAddress.getIsDefault() != 1) {
            addressMapper.update(null, 
                    Wrappers.<JDWAAddress>lambdaUpdate()
                            .eq(JDWAAddress::getUserId, userId)
                            .eq(JDWAAddress::getIsDefault, 1)
                            .set(JDWAAddress::getIsDefault, 0));
        }
        
        addressMapper.updateById(address);
        
        return true;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteAddress(Long addressId) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询地址是否存在
        JDWAAddress existingAddress = addressMapper.selectOne(
                Wrappers.<JDWAAddress>lambdaQuery()
                        .eq(JDWAAddress::getId, addressId)
                        .eq(JDWAAddress::getUserId, userId)
                        .eq(JDWAAddress::getIsDeleted, 0));
        
        if (existingAddress == null) {
            log.error("Address not found for deletion: addressId={}", addressId);
            return false;
        }
        
        // 逻辑删除地址
        existingAddress.setIsDeleted(1);
        existingAddress.setUpdateTime(LocalDateTime.now());
        
        addressMapper.updateById(existingAddress);
        
        // 如果删除的是默认地址,设置最新的地址为默认
        if (existingAddress.getIsDefault() == 1) {
            JDWAAddress newestAddress = addressMapper.selectOne(
                    Wrappers.<JDWAAddress>lambdaQuery()
                            .eq(JDWAAddress::getUserId, userId)
                            .eq(JDWAAddress::getIsDeleted, 0)
                            .orderByDesc(JDWAAddress::getUpdateTime)
                            .last("LIMIT 1"));
            
            if (newestAddress != null) {
                newestAddress.setIsDefault(1);
                newestAddress.setUpdateTime(LocalDateTime.now());
                addressMapper.updateById(newestAddress);
            }
        }
        
        return true;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean setDefaultAddress(Long addressId) {
        // 获取当前登录用户
        Long userId = JDWASecurityUtil.getCurrentUserId();
        
        // 查询地址是否存在
        JDWAAddress existingAddress = addressMapper.selectOne(
                Wrappers.<JDWAAddress>lambdaQuery()
                        .eq(JDWAAddress::getId, addressId)
                        .eq(JDWAAddress::getUserId, userId)
                        .eq(JDWAAddress::getIsDeleted, 0));
        
        if (existingAddress == null) {
            log.error("Address not found for setting default: addressId={}", addressId);
            return false;
        }
        
        // 如果已经是默认地址,不需要操作
        if (existingAddress.getIsDefault() == 1) {
            return true;
        }
        
        // 取消其他默认地址
        addressMapper.update(null, 
                Wrappers.<JDWAAddress>lambdaUpdate()
                        .eq(JDWAAddress::getUserId, userId)
                        .eq(JDWAAddress::getIsDefault, 1)
                        .set(JDWAAddress::getIsDefault, 0));
        
        // 设置为默认地址
        existingAddress.setIsDefault(1);
        existingAddress.setUpdateTime(LocalDateTime.now());
        
        addressMapper.updateById(existingAddress);
        
        return true;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deductUserPoints(Long userId, Integer points, String orderNo, String description) {
        // 调用积分服务扣减用户积分
        return userPointsService.deductPoints(userId, points, "EXCHANGE", orderNo, description);
    }
}

性能优化

高并发处理

奖品兑换模块在活动期间可能面临高并发请求,尤其是限量奖品的发放过程。为了应对这一挑战,我们采取了以下优化措施:

库存锁机制

为防止超卖问题,实现了基于Redis的分布式锁来保证库存操作的原子性:

// Redis分布式锁示例
String lockKey = String.format("prize:stock:lock:%d", prizeId);
Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);

if (Boolean.FALSE.equals(locked)) {
    log.warn("Failed to acquire Redis lock for stock: prizeId={}", prizeId);
    return false;
}

try {
    // 库存操作...
} finally {
    // 释放锁
    redisTemplate.delete(lockKey);
}

异步处理

通过消息队列对非核心流程进行异步处理,减轻系统负担:

// 异步更新奖品兑换数量
@Async
public void asyncUpdateExchangeCount(Long prizeId, Integer quantity) {
    try {
        prizeMapper.increaseExchangeCount(prizeId, quantity);
    } catch (Exception e) {
        log.error("Failed to update exchange count: prizeId={}, quantity={}, error={}",
                prizeId, quantity, e.getMessage(), e);
    }
}

多级缓存

采用多级缓存策略提高热点数据读取效率:

  1. 本地缓存:使用Caffeine缓存热门奖品数据
  2. 分布式缓存:使用Redis缓存奖品列表和分类信息
  3. 缓存预热:系统启动和大促前预热热门奖品缓存

数据库优化

表结构优化

  • 合理设计索引,针对高频查询字段创建索引
  • 使用冷热数据分离策略,将历史订单数据定期归档
  • 采用分库分表策略,按用户ID对订单表进行水平分表

查询优化

  • 使用延迟加载技术,按需加载订单详情和物流信息
  • 实现分页查询,限制大数据量返回
  • 优化统计查询,使用预聚合数据提高统计效率

常见问题与解决方案

库存管理问题

超卖问题

问题描述:在高并发情况下,多个用户同时兑换可能导致库存实际扣减超过可用库存。

解决方案:

  1. 使用Redis分布式锁保证库存操作原子性
  2. 实现悲观锁或乐观锁机制进行并发控制
  3. 数据库层面使用FOR UPDATE锁定库存记录

库存不足处理

问题描述:用户下单后发现实际库存不足。

解决方案:

  1. 建立库存预警机制,当库存低于阈值时自动提醒补货
  2. 实现库存检查与下单的事务处理,保证数据一致性
  3. 对于热门商品,设置排队机制,按顺序分配库存

订单处理问题

订单超时未支付

问题描述:用户创建订单后长时间未支付(适用于组合支付方式)。

解决方案:

  1. 使用延迟队列自动取消超时订单
  2. 释放锁定的库存,返还扣减的积分
  3. 定时任务扫描未支付订单,批量处理
// 订单超时处理示例
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void handleExpiredOrders() {
    List<JDWAExchangeOrder> expiredOrders = orderMapper.selectExpiredOrders(
            JDWAPrizeConstants.ORDER_STATUS_PENDING, LocalDateTime.now().minusMinutes(30));
    
    for (JDWAExchangeOrder order : expiredOrders) {
        try {
            cancelOrder(order.getOrderNo(), "订单超时自动取消");
        } catch (Exception e) {
            log.error("Failed to cancel expired order: orderNo={}, error={}",
                    order.getOrderNo(), e.getMessage(), e);
        }
    }
}

物流跟踪问题

问题描述:无法实时获取物流最新状态。

解决方案:

  1. 与主流物流公司建立API对接,定时同步物流状态
  2. 使用第三方物流聚合服务,统一管理多家物流信息
  3. 实现物流状态变更主动推送机制

兑换资格验证问题

问题描述:复杂的兑换资格规则导致验证逻辑复杂,影响系统性能。

解决方案:

  1. 引入规则引擎,将资格验证规则与业务代码解耦
  2. 对验证结果进行缓存,避免频繁重复验证
  3. 使用批量验证方式,减少数据库查询次数

未来优化规划

短期规划(1-3个月)

  1. 个性化推荐系统

    • 基于用户历史兑换记录和行为数据,实现个性化奖品推荐
    • 引入协同过滤算法,提高推荐精准度
  2. 社交分享功能

    • 允许用户将兑换的奖品分享到社交平台
    • 实现分享邀请好友兑换的奖励机制
  3. 积分兑换活动优化

    • 实现限时兑换活动自动化管理
    • 开发活动预热和预约功能,缓解大促期间系统压力

中期规划(3-6个月)

  1. 多渠道支付集成

    • 集成更多支付方式,支持积分+现金的组合支付
    • 实现支付渠道的动态路由和负载均衡
  2. 智能库存管理

    • 基于历史数据和AI算法,实现库存智能预测
    • 开发自动补货和库存调配系统
  3. 虚拟奖品生态扩展

    • 扩展虚拟奖品类型,如电子书、在线课程等
    • 建立第三方虚拟商品对接平台

长期规划(6-12个月)

  1. 区块链积分兑换

    • 研究区块链技术在积分兑换中的应用
    • 提高积分交易的透明度和安全性
  2. 国际化支持

    • 支持多语言、多币种的奖品兑换
    • 适配国际物流和海外仓储系统
  3. 生态合作伙伴计划

    • 建立开放的API接口,允许合作伙伴接入奖品兑换系统
    • 发展线下实体店兑换网络

总结

JDWA Green-U奖品兑换模块作为碳减排激励体系的重要组成部分,通过将用户碳减排活动获得的积分兑换为实物或虚拟奖励,实现了"绿色行为-积分奖励-奖品兑换"的完整闭环,有效提高了用户参与环保活动的积极性和持续性。

模块设计以用户体验为中心,兼顾系统性能和业务灵活性,成功应对了高并发兑换场景的挑战。通过与积分系统、成就系统等多模块的无缝集成,形成了完整的用户激励体系,为平台的用户增长和活跃度提升提供了有力支撑。

未来,模块将继续优化用户体验,扩展奖品类型,增强个性化推荐能力,并探索更多创新的兑换模式,为用户提供更丰富、更便捷的绿色生活方式转化路径,助力实现JDWA Green-U平台的环保价值主张和业务目标。

API接口规范

奖品管理接口

获取奖品列表

  • 接口URL: /api/prizes/list

  • 请求方式: GET

  • 请求参数:

    参数名类型必须描述
    categoryIdLong否分类ID
    prizeTypeString否奖品类型:PHYSICAL/VIRTUAL/COUPON/PRIVILEGE/DONATION
    prizeLevelString否奖品等级:COMMON/PREMIUM/RARE/EXCLUSIVE
    keywordString否搜索关键词
    pageNumInteger否页码,默认1
    pageSizeInteger否每页大小,默认10
  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": {
        "records": [
          {
            "id": 1,
            "prizeCode": "P20251001001",
            "prizeName": "环保购物袋",
            "prizeType": "PHYSICAL",
            "prizeLevel": "COMMON",
            "categoryId": 1,
            "pointsPrice": 100,
            "marketPrice": 15.00,
            "cashPrice": null,
            "thumbnail": "http://example.com/images/bag.jpg",
            "description": "环保材质购物袋,减少塑料使用",
            "availableStock": 100,
            "hasEnoughPoints": true
          }
        ],
        "total": 10,
        "pageNum": 1,
        "pageSize": 10
      }
    }
    

获取奖品详情

  • 接口URL: /api/prizes/detail/{prizeCode}

  • 请求方式: GET

  • 请求参数:

    参数名类型必须描述
    prizeCodeString是奖品编码
  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": {
        "id": 1,
        "prizeCode": "P20251001001",
        "prizeName": "环保购物袋",
        "prizeType": "PHYSICAL",
        "prizeLevel": "COMMON",
        "categoryId": 1,
        "categoryName": "环保用品",
        "pointsPrice": 100,
        "marketPrice": 15.00,
        "cashPrice": null,
        "thumbnail": "http://example.com/images/bag.jpg",
        "images": ["http://example.com/images/bag1.jpg", "http://example.com/images/bag2.jpg"],
        "description": "环保材质购物袋,减少塑料使用",
        "specification": "尺寸: 40x30cm,材质: 可降解环保材料",
        "exchangeLimit": 5,
        "exchangeStart": "2025-10-01 00:00:00",
        "exchangeEnd": "2025-12-31 23:59:59",
        "weight": 0.2,
        "deliveryFee": 0,
        "availableStock": 100,
        "totalStock": 150,
        "userCurrentPoints": 300,
        "hasEnoughPoints": true,
        "canExchange": true,
        "rules": [
          {
            "id": 1,
            "ruleCode": "LIMIT_001",
            "ruleName": "每人限兑5个",
            "ruleType": "LIMIT",
            "ruleCondition": "{\"limit\":5}",
            "ruleValue": "5",
            "description": "每个用户最多可兑换5个"
          }
        ]
      }
    }
    

购物车接口

添加到购物车

  • 接口URL: /api/prizes/cart/add

  • 请求方式: POST

  • 请求参数:

    {
      "prizeId": 1,
      "quantity": 2
    }
    
  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": null
    }
    

购物车列表

  • 接口URL: /api/prizes/cart/list

  • 请求方式: GET

  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": [
        {
          "id": 1,
          "prizeId": 1,
          "prizeCode": "P20251001001",
          "prizeName": "环保购物袋",
          "prizeType": "PHYSICAL",
          "prizeImage": "http://example.com/images/bag.jpg",
          "pointsPrice": 100,
          "cashPrice": null,
          "quantity": 2,
          "totalPoints": 200,
          "totalCash": 0,
          "prizeStatus": 1,
          "canExchange": true,
          "availableStock": 100,
          "stockEnough": true,
          "selectedStatus": 1
        }
      ]
    }
    

订单接口

创建兑换订单

  • 接口URL: /api/prizes/order/create

  • 请求方式: POST

  • 请求参数:

    {
      "items": [
        {
          "prizeId": 1,
          "quantity": 2
        }
      ],
      "addressId": 10,
      "deliveryMethod": "EXPRESS",
      "cartItemIds": [1],
      "remark": "请包装好,谢谢"
    }
    
  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": "JD2025101012345678901234"
    }
    

获取订单列表

  • 接口URL: /api/prizes/order/list

  • 请求方式: GET

  • 请求参数:

    参数名类型必须描述
    statusString否订单状态
    pageNumInteger否页码,默认1
    pageSizeInteger否每页大小,默认10
  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": {
        "records": [
          {
            "id": 1,
            "orderNo": "JD2025101012345678901234",
            "orderType": "PHYSICAL",
            "totalPoints": 200,
            "totalCash": 0,
            "totalAmount": 2,
            "status": "PENDING",
            "createTime": "2025-10-10 12:34:56",
            "firstPrizeImage": "http://example.com/images/bag.jpg",
            "items": [
              {
                "id": 1,
                "orderId": 1,
                "orderNo": "JD2025101012345678901234",
                "prizeId": 1,
                "prizeName": "环保购物袋",
                "prizeType": "PHYSICAL",
                "prizeImage": "http://example.com/images/bag.jpg",
                "pointsPrice": 100,
                "quantity": 2,
                "totalPoints": 200,
                "status": "PENDING"
              }
            ]
          }
        ],
        "total": 10,
        "pageNum": 1,
        "pageSize": 10
      }
    }
    

获取订单详情

  • 接口URL: /api/prizes/order/detail/{orderNo}

  • 请求方式: GET

  • 请求参数:

    参数名类型必须描述
    orderNoString是订单编号
  • 响应结果:

    {
      "code": 200,
      "message": "操作成功",
      "data": {
        "id": 1,
        "orderNo": "JD2025101012345678901234",
        "orderType": "PHYSICAL",
        "totalPoints": 200,
        "totalCash": 0,
        "totalAmount": 2,
        "status": "PENDING",
        "paymentStatus": 1,
        "paymentTime": "2025-10-10 12:35:12",
        "deliveryMethod": "EXPRESS",
        "deliveryFee": 0,
        "consignee": "张三",
        "consigneePhone": "13800138000",
        "consigneeAddress": "北京市海淀区中关村南大街5号",
        "logisticsCompany": "顺丰快递",
        "logisticsNo": "SF1234567890",
        "createTime": "2025-10-10 12:34:56",
        "remark": "请包装好,谢谢",
        "items": [
          {
            "id": 1,
            "orderId": 1,
            "orderNo": "JD2025101012345678901234",
            "prizeId": 1,
            "prizeName": "环保购物袋",
            "prizeType": "PHYSICAL",
            "prizeImage": "http://example.com/images/bag.jpg",
            "pointsPrice": 100,
            "quantity": 2,
            "totalPoints": 200,
            "status": "PENDING"
          }
        ],
        "logistics": {
          "logisticsCompany": "顺丰快递",
          "logisticsNo": "SF1234567890",
          "status": "SHIPPED",
          "trackingList": [
            {
              "time": "2025-10-11 09:30:00",
              "status": "已揽收",
              "description": "快递员已揽收"
            },
            {
              "time": "2025-10-11 12:45:00",
              "status": "运输中",
              "description": "货物已到达分拣中心"
            }
          ]
        }
      }
    }
    

系统集成

奖品兑换模块与其他系统模块紧密集成,主要包括以下方面:

用户积分系统

  • 积分验证:兑换前验证用户积分是否足够
  • 积分扣减:兑换成功后,扣减用户相应积分
  • 积分返还:订单取消时,返还已扣减的积分

成就系统

  • 成就条件验证:部分特殊奖品需要用户达成特定成就才能兑换
  • 兑换触发成就:特定奖品兑换可能触发特定成就解锁

活动模块

  • 活动奖品发放:活动模块可通过奖品兑换模块向用户发放活动奖励
  • 活动资格验证:部分奖品需要用户参与特定活动才能兑换

库存管理系统

  • 实时库存监控:监控奖品库存,及时发现库存不足情况
  • 库存预警:当库存低于预警阈值时,自动通知管理员补充库存
  • 库存锁定机制:在下单过程中锁定库存,防止超卖

物流配送系统

  • 物流订单创建:实物奖品兑换成功后,自动创建物流订单
  • 物流信息查询:提供物流信息实时查询功能
  • 配送通知:物流状态更新时,向用户推送配送通知

用户通知系统

  • 兑换成功通知:奖品兑换成功后,向用户发送通知
  • 订单状态变更通知:订单状态变更时,实时通知用户
  • 虚拟奖品激活通知:虚拟奖品激活时,发送激活信息

业务流程示例

以下是一个完整的奖品兑换流程示例:

实物奖品兑换流程

  1. 用户浏览奖品:用户在平台浏览可兑换的奖品列表
  2. 选择奖品加入购物车:用户选择心仪的奖品加入购物车
  3. 提交兑换订单:
    • 用户确认购物车中的奖品并提交兑换订单
    • 系统验证用户积分是否足够
    • 系统验证奖品库存是否充足
    • 系统验证用户是否满足兑换资格
  4. 订单创建与积分扣减:
    • 创建兑换订单记录
    • 锁定奖品库存
    • 扣减用户积分
  5. 后台审核与发货:
    • 管理员审核订单
    • 准备奖品并安排发货
    • 录入物流信息
  6. 用户收货确认:
    • 用户收到奖品后确认收货
    • 系统完成订单流程,更新订单状态为已完成

虚拟奖品兑换流程

  1. 用户选择虚拟奖品:用户选择虚拟奖品(如优惠券、会员特权等)
  2. 提交兑换请求:用户提交虚拟奖品兑换请求
  3. 系统自动处理:
    • 验证用户积分和兑换资格
    • 扣减用户积分
    • 自动生成虚拟奖品兑换码或权益
  4. 用户激活使用:
    • 用户接收到虚拟奖品信息
    • 根据指引激活或使用虚拟奖品

兑换规则引擎

奖品兑换模块包含一个灵活的规则引擎,用于管理各种兑换限制和优惠规则:

规则类型

  1. 积分优惠规则

    • 限时折扣:在特定时间段内,减少兑换所需积分
    • 会员折扣:根据用户等级提供积分优惠
    • 套装优惠:购买多件同类奖品时提供积分折扣
  2. 兑换限制规则

    • 每人限量:限制每位用户的最大兑换数量
    • 每日限量:限制每日平台兑换总量
    • 新用户限制:对新注册用户的兑换权限进行限制
  3. 资格限制规则

    • 等级要求:需要达到特定用户等级才能兑换
    • 成就要求:需要解锁特定成就才能兑换
    • 活动要求:需要参与特定活动才能兑换

规则执行机制

规则引擎采用优先级排序的方式执行规则:

  1. 根据规则优先级对适用规则进行排序
  2. 按优先级从高到低依次应用规则
  3. 对于互斥规则,只应用优先级最高的规则
  4. 兑换资格规则必须全部满足才能通过验证
Prev
JDWA Green-U 成就系统模块技术文档
Next
JDWA Green-U API接口文档