Просмотр исходного кода

Merge remote-tracking branch 'origin/sit-marketing' into sit-marketing

fcw 2 месяцев назад
Родитель
Сommit
b6feefaab4
13 измененных файлов с 331 добавлено и 66 удалено
  1. 14 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityAchievementCaseVo.java
  2. 5 2
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityDetailVo.java
  3. 16 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityMySignupVo.java
  4. 6 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityVO.java
  5. 10 0
      alien-entity/src/main/java/shop/alien/mapper/storePlantform/StoreOperationalActivitySignupMapper.java
  6. 14 2
      alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivityAchievementMapper.xml
  7. 23 2
      alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivitySignupMapper.xml
  8. 1 1
      alien-store/src/main/java/shop/alien/store/controller/OperationalActivityController.java
  9. 37 13
      alien-store/src/main/java/shop/alien/store/controller/StoreOperationalActivityController.java
  10. 8 5
      alien-store/src/main/java/shop/alien/store/service/OperationalActivityService.java
  11. 10 1
      alien-store/src/main/java/shop/alien/store/service/StoreOperationalActivityService.java
  12. 41 8
      alien-store/src/main/java/shop/alien/store/service/impl/OperationalActivityServiceImpl.java
  13. 146 32
      alien-store/src/main/java/shop/alien/store/service/impl/StoreOperationalActivityServiceImpl.java

+ 14 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityAchievementCaseVo.java

@@ -31,12 +31,26 @@ public class StoreOperationalActivityAchievementCaseVo {
     @ApiModelProperty(value = "活动名称")
     private String activityName;
 
+    @ApiModelProperty(value = "活动开始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date startTime;
+
+    @ApiModelProperty(value = "活动结束时间")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date endTime;
+
     @ApiModelProperty(value = "活动状态")
     private Integer activityStatus;
 
     @ApiModelProperty(value = "用户昵称")
     private String nickName;
 
+    @ApiModelProperty(value = "店铺用户头像")
+    private String storeUserHeadImg;
+
+    @ApiModelProperty(value = "店铺用户昵称")
+    private String storeUserNickName;
+
     @ApiModelProperty(value = "成果首图")
     private String firstMediaUrl;
 

+ 5 - 2
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityDetailVo.java

@@ -109,8 +109,11 @@ public class StoreOperationalActivityDetailVo {
     @ApiModelProperty(value = "是否已报名")
     private Boolean signedUp;
 
-    @ApiModelProperty(value = "报名是否截止")
-    private Boolean signupExpired;
+    @ApiModelProperty(value = "是否在报名时间内")
+    private Boolean inSignupTime;
+
+    @ApiModelProperty(value = "详情按钮状态:0-活动已结束,1-报名已截止,2-等待活动开始,3-报名成功,4-已报名,5-立即报名")
+    private Integer detailStatus;
 
     @ApiModelProperty(value = "结果类型:0-文字, 1-图片")
     private Integer resultType;

+ 16 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityMySignupVo.java

@@ -1,6 +1,7 @@
 package shop.alien.entity.storePlatform.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -22,6 +23,9 @@ public class StoreOperationalActivityMySignupVo {
     @ApiModelProperty(value = "活动ID")
     private Integer activityId;
 
+    @ApiModelProperty(value = "报名ID")
+    private Integer signupId;
+
     @ApiModelProperty(value = "商户ID")
     private Integer storeId;
 
@@ -34,6 +38,12 @@ public class StoreOperationalActivityMySignupVo {
     @ApiModelProperty(value = "活动标题图URL")
     private String activityTitleImgUrl;
 
+    @ApiModelProperty(value = "店铺用户头像")
+    private String storeUserHeadImg;
+
+    @ApiModelProperty(value = "店铺用户昵称")
+    private String storeUserNickName;
+
     @ApiModelProperty(value = "活动开始时间")
     @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
     private Date startTime;
@@ -48,12 +58,18 @@ public class StoreOperationalActivityMySignupVo {
     @ApiModelProperty(value = "报名状态")
     private Integer signupStatus;
 
+    @JsonIgnore
+    private Integer signupAuditStatus;
+
     @ApiModelProperty(value = "活动开始状态(0-已开始,1-已结束)")
     private Integer activityStartStatus;
 
     @ApiModelProperty(value = "是否有成果")
     private Integer hasAchievement;
 
+    @ApiModelProperty(value = "列表状态")
+    private Integer status;
+
     @ApiModelProperty(value = "报名时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date signupTime;

+ 6 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityVO.java

@@ -54,5 +54,11 @@ public class StoreOperationalActivityVO extends StoreOperationalActivity {
 
     @ApiModelProperty(value = "活动详情图片")
     private String activityDetailImgUrl;
+
+    @ApiModelProperty(value = "活动详情图片URL列表")
+    private java.util.List<String> activityDetailImgUrlList;
+
+    @ApiModelProperty(value = "活动类型名称")
+    private String activityTypeName;
 }
 

+ 10 - 0
alien-entity/src/main/java/shop/alien/mapper/storePlantform/StoreOperationalActivitySignupMapper.java

@@ -62,6 +62,16 @@ public interface StoreOperationalActivitySignupMapper extends BaseMapper<StoreOp
                                            @Param("userId") Integer userId);
 
     /**
+     * 获取用户最新报名状态。
+     *
+     * @param activityId 活动ID
+     * @param userId     用户ID
+     * @return 报名状态
+     */
+    Integer selectLatestSignupStatus(@Param("activityId") Integer activityId,
+                                     @Param("userId") Integer userId);
+
+    /**
      * 查询我的报名列表。
      *
      * @param userId 用户ID

+ 14 - 2
alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivityAchievementMapper.xml

@@ -8,13 +8,17 @@
             ach.user_id AS userId,
             ach.activity_id AS activityId,
             act.activity_name AS activityName,
+            act.start_time AS startTime,
+            act.end_time AS endTime,
             CASE
                 WHEN act.status = 7 THEN 7
                 WHEN act.status IN (4, 5) AND act.end_time IS NOT NULL AND NOW() > act.end_time THEN 7
-                WHEN act.status IN (4, 5) AND (act.start_time IS NULL OR NOW() >= act.start_time) THEN 5
+                WHEN act.status IN (4, 5) AND (act.start_time IS NULL OR (NOW() >= act.start_time AND act.end_time > NOW())) THEN 5
                 ELSE act.status
             END AS activityStatus,
             u.user_name AS nickName,
+            su.head_img AS storeUserHeadImg,
+            su.nick_name AS storeUserNickName,
             SUBSTRING_INDEX(ach.media_urls, ',', 1) AS firstMediaUrl,
             COALESCE(ach.updated_time, ach.created_time) AS updatedTime
         FROM store_operational_activity_achievement ach
@@ -35,10 +39,18 @@
         ) latest ON latest.max_id = ach.id
         INNER JOIN store_operational_activity act ON act.id = ach.activity_id
         LEFT JOIN life_user u ON u.id = ach.user_id
+        LEFT JOIN store_user su ON su.store_id = act.store_id
+            AND su.account_type = 1
+            AND su.delete_flag = 0
         WHERE ach.delete_flag = 0
           AND act.delete_flag = 0
         <if test="activityStatus != null">
-            AND act.status = #{activityStatus}
+            <if test="activityStatus == 5">
+                AND act.status IN (4, 5)
+            </if>
+            <if test="activityStatus != 5">
+                AND act.status = #{activityStatus}
+            </if>
         </if>
         ORDER BY COALESCE(ach.updated_time, ach.created_time) DESC, ach.id DESC
     </select>

+ 23 - 2
alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivitySignupMapper.xml

@@ -70,19 +70,37 @@
         LIMIT 1
     </select>
 
+    <select id="selectLatestSignupStatus" resultType="java.lang.Integer">
+        SELECT status
+        FROM store_operational_activity_signup
+        WHERE activity_id = #{activityId}
+          AND user_id = #{userId}
+          AND delete_flag = 0
+        ORDER BY signup_time DESC, id DESC
+        LIMIT 1
+    </select>
+
     <select id="selectMySignups" resultType="shop.alien.entity.storePlatform.vo.StoreOperationalActivityMySignupVo">
         SELECT
+            s.id AS signupId,
             s.activity_id AS activityId,
             a.store_id AS storeId,
             a.activity_name AS activityName,
             a.promotional_image AS promotionalImage,
             img.img_url AS activityTitleImgUrl,
+            su.head_img AS storeUserHeadImg,
+            su.nick_name AS storeUserNickName,
             a.start_time AS startTime,
             a.end_time AS endTime,
             a.status AS activityStatus,
-            s.status AS signupStatus,
+            CASE
+                WHEN a.signup_start_time IS NOT NULL AND NOW() &lt; a.signup_start_time THEN 0
+                WHEN a.signup_end_time IS NOT NULL AND NOW() &gt; a.signup_end_time THEN 2
+                ELSE 1
+            END AS signupStatus,
+            s.status AS signupAuditStatus,
             CASE WHEN a.status = 7 THEN 1 ELSE 0 END AS activityStartStatus,
-            CASE WHEN ach.id IS NULL THEN 0 ELSE 1 END AS hasAchievement,
+            CASE WHEN ach.signup_id IS NULL THEN 0 ELSE 1 END AS hasAchievement,
             s.signup_time AS signupTime
         FROM store_operational_activity_signup s
         INNER JOIN store_operational_activity a ON a.id = s.activity_id
@@ -90,6 +108,9 @@
             AND img.business_id = a.id
             AND img.img_type = 26
             AND img.delete_flag = 0
+        LEFT JOIN store_user su ON su.store_id = a.store_id
+            AND su.account_type = 1
+            AND su.delete_flag = 0
         LEFT JOIN (
             SELECT DISTINCT signup_id
             FROM store_operational_activity_achievement

+ 1 - 1
alien-store/src/main/java/shop/alien/store/controller/OperationalActivityController.java

@@ -63,7 +63,7 @@ public class OperationalActivityController {
             @RequestParam(value = "activityName", required = false) String activityName) {
         log.info("OperationalActivityController.pageActivityDetail storeId={}, storeName={}, pageNum={}, pageSize={}, status={}, activityName={}", storeId, storeName, pageNum, pageSize, status, activityName);
         try {
-            IPage<StoreOperationalActivityVO> result = activityService.pageActivityDetail(storeId, storeName, pageNum, pageSize, status, activityName);
+            IPage<StoreOperationalActivityVO> result = activityService.pageActivityDetail(storeId, storeName, pageNum, pageSize, status, activityName, null);
             return R.data(result);
         } catch (IllegalArgumentException e) {
             return R.fail(e.getMessage());

+ 37 - 13
alien-store/src/main/java/shop/alien/store/controller/StoreOperationalActivityController.java

@@ -28,8 +28,6 @@ import shop.alien.store.dto.StoreOperationalActivitySignupDto;
 import shop.alien.store.dto.StoreOperationalActivityAchievementDto;
 import shop.alien.store.service.StoreOperationalActivityService;
 import shop.alien.store.service.StoreOperationalActivityAchievementService;
-import shop.alien.util.common.JwtUtil;
-import com.alibaba.fastjson.JSONObject;
 
 /**
  * 运营活动详情控制器
@@ -51,12 +49,16 @@ public class StoreOperationalActivityController {
 
     @ApiOperation("查询活动详情")
     @ApiOperationSupport(order = 1)
-    @ApiImplicitParam(name = "id", value = "活动ID", dataTypeClass = Integer.class, paramType = "query", required = true)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "活动ID", dataTypeClass = Integer.class, paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataTypeClass = Integer.class, paramType = "query", required = true)
+    })
     @GetMapping("/detail")
-    public R<StoreOperationalActivityDetailVo> getActivityDetail(@RequestParam("id") Integer id) {
-        log.info("StoreOperationalActivityController.getActivityDetail id={}", id);
+    public R<StoreOperationalActivityDetailVo> getActivityDetail(@RequestParam("id") Integer id,
+                                                                 @RequestParam("userId") Integer userId) {
+        log.info("StoreOperationalActivityController.getActivityDetail id={}, userId={}", id, userId);
         try {
-            StoreOperationalActivityDetailVo result = operationalActivityService.getActivityDetail(id);
+            StoreOperationalActivityDetailVo result = operationalActivityService.getActivityDetail(id, userId);
             return R.data(result);
         } catch (IllegalArgumentException e) {
             return R.fail(e.getMessage());
@@ -212,15 +214,17 @@ public class StoreOperationalActivityController {
 
     @ApiOperation("我的报名列表")
     @ApiOperationSupport(order = 9)
+    @ApiImplicitParam(name = "userId", value = "用户ID", dataTypeClass = Integer.class, paramType = "query", required = true)
     @GetMapping("/signup/my")
-    public R<List<StoreOperationalActivityMySignupVo>> listMySignups() {
+    public R<List<StoreOperationalActivityMySignupVo>> listMySignups(@RequestParam("userId") Integer userId) {
+        /*
+         * 我的报名列表接口:
+         * 1. userId 必传,作为报名列表与状态计算的唯一依据。
+         * 2. 由服务层负责聚合活动信息、报名审核状态、成果状态等字段。
+         * 3. 接口仅做参数透传与异常转换,避免在控制器重复业务判断。
+         */
         try {
-            JSONObject userInfo = JwtUtil.getCurrentUserInfo();
-            if (userInfo == null || userInfo.getInteger("userId") == null) {
-                return R.fail("用户未登录");
-            }
-            List<StoreOperationalActivityMySignupVo> result =
-                    operationalActivityService.listMySignups(userInfo.getInteger("userId"));
+            List<StoreOperationalActivityMySignupVo> result = operationalActivityService.listMySignups(userId);
             return R.data(result);
         } catch (IllegalArgumentException e) {
             return R.fail(e.getMessage());
@@ -229,4 +233,24 @@ public class StoreOperationalActivityController {
             return R.fail(e.getMessage());
         }
     }
+
+    @ApiOperation("删除报名及成果")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "activityId", value = "活动ID", dataTypeClass = Integer.class, paramType = "query", required = true),
+            @ApiImplicitParam(name = "signupId", value = "报名ID", dataTypeClass = Integer.class, paramType = "query", required = true)
+    })
+    @PostMapping("/signup/delete")
+    public R<String> deleteSignup(@RequestParam("activityId") Integer activityId,
+                                  @RequestParam("signupId") Integer signupId) {
+        try {
+            boolean result = operationalActivityService.deleteSignup(activityId, signupId);
+            return result ? R.success("删除成功") : R.fail("删除失败");
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("StoreOperationalActivityController.deleteSignup ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
 }

+ 8 - 5
alien-store/src/main/java/shop/alien/store/service/OperationalActivityService.java

@@ -24,13 +24,16 @@ public interface OperationalActivityService {
     /**
      * 根据门店条件分页查询运营活动
      *
-     * @param storeId   门店ID
-     * @param storeName 门店名称(模糊)
-     * @param pageNum   页码
-     * @param pageSize  每页数量
+     * @param storeId      门店ID
+     * @param storeName    门店名称(模糊)
+     * @param pageNum      页码
+     * @param pageSize     每页数量
+     * @param activityStatus 活动状态
+     * @param activityName 活动名称(模糊)
+     * @param activityType 活动类型(COMMENT-评论有礼, MARKETING-营销活动)
      * @return 活动分页结果
      */
-    IPage<StoreOperationalActivityVO> pageActivityDetail(Integer storeId, String storeName, Integer pageNum, Integer pageSize, Integer activityStatus, String activityName);
+    IPage<StoreOperationalActivityVO> pageActivityDetail(Integer storeId, String storeName, Integer pageNum, Integer pageSize, Integer activityStatus, String activityName, String activityType);
 
     /**
      * 根据活动ID获取活动详情

+ 10 - 1
alien-store/src/main/java/shop/alien/store/service/StoreOperationalActivityService.java

@@ -20,7 +20,7 @@ public interface StoreOperationalActivityService {
      * @param id 活动ID
      * @return 活动详情
      */
-    StoreOperationalActivityDetailVo getActivityDetail(Integer id);
+    StoreOperationalActivityDetailVo getActivityDetail(Integer id, Integer userId);
 
     /**
      * 报名校验:是否已满、是否已成功报名。
@@ -40,6 +40,15 @@ public interface StoreOperationalActivityService {
     List<StoreOperationalActivityMySignupVo> listMySignups(Integer userId);
 
     /**
+     * 删除报名及其成果
+     *
+     * @param activityId 活动ID
+     * @param signupId 报名ID
+     * @return 是否成功
+     */
+    boolean deleteSignup(Integer activityId, Integer signupId);
+
+    /**
      * 活动报名
      *
      * @param dto 报名请求

+ 41 - 8
alien-store/src/main/java/shop/alien/store/service/impl/OperationalActivityServiceImpl.java

@@ -79,8 +79,8 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
     }
 
     @Override
-    public IPage<StoreOperationalActivityVO> pageActivityDetail(Integer storeId, String storeName, Integer pageNum, Integer pageSize, Integer activityStatus, String activityName) {
-        log.info("OperationalActivityServiceImpl.pageActivityDetail: storeId={}, storeName={}, pageNum={}, pageSize={}, activityStatus={}, activityName={}", storeId, storeName, pageNum, pageSize, activityStatus, activityName);
+    public IPage<StoreOperationalActivityVO> pageActivityDetail(Integer storeId, String storeName, Integer pageNum, Integer pageSize, Integer activityStatus, String activityName, String activityType) {
+        log.info("OperationalActivityServiceImpl.pageActivityDetail: storeId={}, storeName={}, pageNum={}, pageSize={}, activityStatus={}, activityName={}, activityType={}", storeId, storeName, pageNum, pageSize, activityStatus, activityName, activityType);
 
 //        if (storeId == null && StringUtils.isBlank(storeName)) {
 //            throw new IllegalArgumentException("请至少提供商户ID或商户名称");
@@ -118,6 +118,9 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         if (StringUtils.isNotBlank(activityName)) {
             wrapper.like(StoreOperationalActivity::getActivityName, activityName);
         }
+        if (StringUtils.isNotBlank(activityType)) {
+            wrapper.eq(StoreOperationalActivity::getActivityType, activityType);
+        }
 //
 //        if (!storeIds.isEmpty()) {
 //            wrapper.in(StoreOperationalActivity::getStoreId, storeIds);
@@ -134,6 +137,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
             StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
             BeanUtils.copyProperties(activity, vo);
             vo.setStatusName(resolveStatusName(activity.getStatus()));
+            vo.setActivityTypeName(resolveActivityTypeName(activity.getActivityType()));
 
             if (activity.getCouponId() != null) {
                 LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
@@ -168,6 +172,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
         BeanUtils.copyProperties(activity, vo);
         vo.setStatusName(resolveStatusName(activity.getStatus()));
+        vo.setActivityTypeName(resolveActivityTypeName(activity.getActivityType()));
 
         if (activity.getCouponId() != null) {
             LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
@@ -241,24 +246,52 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         if (vo == null || vo.getStoreId() == null || vo.getId() == null) {
             return;
         }
+        // 查询活动标题图(单张)
         StoreImg titleImg = imgMapper.selectOne(new LambdaQueryWrapper<StoreImg>()
                 .eq(StoreImg::getStoreId, vo.getStoreId())
                 .eq(StoreImg::getBusinessId, vo.getId())
                 .eq(StoreImg::getImgType, 26)
-                .eq(StoreImg::getDeleteFlag, 0));
+                .eq(StoreImg::getDeleteFlag, 0)
+                .orderByAsc(StoreImg::getImgSort)
+                .last("LIMIT 1"));
         if (titleImg != null) {
             vo.setActivityTitleImg(titleImg);
             vo.setActivityTitleImgUrl(titleImg.getImgUrl());
         }
 
-        StoreImg detailImg = imgMapper.selectOne(new LambdaQueryWrapper<StoreImg>()
+        // 查询活动详情图(多张)
+        List<StoreImg> detailImgs = imgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
                 .eq(StoreImg::getStoreId, vo.getStoreId())
                 .eq(StoreImg::getBusinessId, vo.getId())
                 .eq(StoreImg::getImgType, 27)
-                .eq(StoreImg::getDeleteFlag, 0));
-        if (detailImg != null) {
-            vo.setActivityDetailImg(detailImg);
-            vo.setActivityDetailImgUrl(detailImg.getImgUrl());
+                .eq(StoreImg::getDeleteFlag, 0)
+                .orderByAsc(StoreImg::getImgSort));
+        if (detailImgs != null && !detailImgs.isEmpty()) {
+            // 设置第一张详情图(兼容旧字段)
+            vo.setActivityDetailImg(detailImgs.get(0));
+            vo.setActivityDetailImgUrl(detailImgs.get(0).getImgUrl());
+            // 设置详情图列表
+            List<String> detailImgUrls = new ArrayList<>();
+            for (StoreImg img : detailImgs) {
+                if (img != null && img.getImgUrl() != null && !img.getImgUrl().trim().isEmpty()) {
+                    detailImgUrls.add(img.getImgUrl().trim());
+                }
+            }
+            vo.setActivityDetailImgUrlList(detailImgUrls);
+        }
+    }
+
+    private String resolveActivityTypeName(String activityType) {
+        if (StringUtils.isBlank(activityType)) {
+            return null;
+        }
+        switch (activityType) {
+            case "COMMENT":
+                return "评论有礼";
+            case "MARKETING":
+                return "营销活动";
+            default:
+                return activityType;
         }
     }
 }

+ 146 - 32
alien-store/src/main/java/shop/alien/store/service/impl/StoreOperationalActivityServiceImpl.java

@@ -8,6 +8,7 @@ import shop.alien.entity.store.LifeDiscountCoupon;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.entity.store.StoreInfo;
 import shop.alien.entity.storePlatform.StoreOperationalActivity;
+import shop.alien.entity.storePlatform.StoreOperationalActivityAchievement;
 import shop.alien.entity.storePlatform.StoreOperationalActivitySignup;
 import shop.alien.entity.storePlatform.vo.StoreOperationalActivityDetailVo;
 import shop.alien.entity.storePlatform.vo.StoreOperationalActivityMySignupVo;
@@ -15,14 +16,13 @@ import shop.alien.entity.storePlatform.vo.StoreOperationalActivitySignupCheckVo;
 import shop.alien.mapper.LifeDiscountCouponMapper;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.StoreInfoMapper;
+import shop.alien.mapper.storePlantform.StoreOperationalActivityAchievementMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivitySignupMapper;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.dto.StoreOperationalActivitySignupDto;
 import shop.alien.store.service.StoreOperationalActivityService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.alibaba.fastjson.JSONObject;
-import shop.alien.util.common.JwtUtil;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -41,6 +41,7 @@ import java.util.List;
 public class StoreOperationalActivityServiceImpl implements StoreOperationalActivityService {
 
     private final StoreOperationalActivityMapper activityMapper;
+    private final StoreOperationalActivityAchievementMapper achievementMapper;
     private final StoreImgMapper imgMapper;
     private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
     private final StoreInfoMapper storeInfoMapper;
@@ -48,8 +49,8 @@ public class StoreOperationalActivityServiceImpl implements StoreOperationalActi
     private final BaseRedisService baseRedisService;
 
     @Override
-    public StoreOperationalActivityDetailVo getActivityDetail(Integer id) {
-        log.info("StoreOperationalActivityServiceImpl.getActivityDetail: id={}", id);
+    public StoreOperationalActivityDetailVo getActivityDetail(Integer id, Integer userId) {
+        log.info("StoreOperationalActivityServiceImpl.getActivityDetail: id={}, userId={}", id, userId);
         if (id == null) {
             throw new IllegalArgumentException("活动ID不能为空");
         }
@@ -61,8 +62,10 @@ public class StoreOperationalActivityServiceImpl implements StoreOperationalActi
         StoreOperationalActivityDetailVo vo = new StoreOperationalActivityDetailVo();
         BeanUtils.copyProperties(activity, vo);
         vo.setStatusName(resolveStatusName(activity.getStatus()));
-        fillSignupFlags(vo, activity);
+        fillSignupFlags(vo, activity, userId);
         fillSignupCounts(vo);
+        Integer signupStatus = resolveCurrentUserSignupStatus(activity.getId(), userId);
+        vo.setDetailStatus(resolveDetailStatus(activity, signupStatus));
 
         if (activity.getCouponId() != null) {
             LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
@@ -107,6 +110,12 @@ public class StoreOperationalActivityServiceImpl implements StoreOperationalActi
 
     @Override
     public List<StoreOperationalActivityMySignupVo> listMySignups(Integer userId) {
+        /**
+         * 我的报名列表:
+         * - 聚合活动基础信息、报名审核状态、成果状态等字段;
+         * - 计算列表展示状态(status)与状态文案(statusLabel);
+         * - 只返回未删除的数据。
+         */
         if (userId == null) {
             throw new IllegalArgumentException("用户ID不能为空");
         }
@@ -118,12 +127,32 @@ public class StoreOperationalActivityServiceImpl implements StoreOperationalActi
             if (item == null) {
                 continue;
             }
+            // 基于报名时间窗口生成展示文案(报名未开始/报名中/报名已结束)
             item.setStatusLabel(resolveSignupStatusLabel(item.getSignupStatus(), item.getActivityStatus()));
+            // 基于活动状态、报名审核状态与成果状态计算列表展示状态
+            item.setStatus(resolveMySignupStatus(item));
         }
         return list;
     }
 
     @Override
+    public boolean deleteSignup(Integer activityId, Integer signupId) {
+        if (activityId == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+        if (signupId == null) {
+            throw new IllegalArgumentException("报名ID不能为空");
+        }
+        int deletedSignup = signupMapper.delete(new LambdaQueryWrapper<StoreOperationalActivitySignup>()
+                .eq(StoreOperationalActivitySignup::getId, signupId)
+                .eq(StoreOperationalActivitySignup::getActivityId, activityId));
+        int deletedAchievement = achievementMapper.delete(new LambdaQueryWrapper<StoreOperationalActivityAchievement>()
+                .eq(StoreOperationalActivityAchievement::getSignupId, signupId)
+                .eq(StoreOperationalActivityAchievement::getActivityId, activityId));
+        return deletedSignup > 0 || deletedAchievement > 0;
+    }
+
+    @Override
     public boolean signup(StoreOperationalActivitySignupDto dto) {
         if (dto == null) {
             throw new IllegalArgumentException("报名参数不能为空");
@@ -268,32 +297,82 @@ public class StoreOperationalActivityServiceImpl implements StoreOperationalActi
         vo.setCurrentApprovedCount(approvedCount != null ? approvedCount : 0);
     }
 
-    private void fillSignupFlags(StoreOperationalActivityDetailVo vo, StoreOperationalActivity activity) {
+    private void fillSignupFlags(StoreOperationalActivityDetailVo vo, StoreOperationalActivity activity, Integer userId) {
         if (vo == null || activity == null) {
             return;
         }
-        boolean expired = false;
-        if (activity.getSignupEndTime() != null) {
-            expired = new Date().after(activity.getSignupEndTime());
+        vo.setInSignupTime(resolveInSignupTime(activity));
+        if (userId == null) {
+            vo.setSignedUp(false);
+            return;
         }
-        vo.setSignupExpired(expired);
+        Integer signupCount = signupMapper.countSignedUpByActivityAndUser(activity.getId(), userId);
+        vo.setSignedUp(signupCount != null && signupCount > 0);
+    }
 
-        Integer currentUserId = null;
-        try {
-            JSONObject userInfo = JwtUtil.getCurrentUserInfo();
-            if (userInfo != null) {
-                currentUserId = userInfo.getInteger("userId");
+    private boolean resolveInSignupTime(StoreOperationalActivity activity) {
+        if (activity == null) {
+            return false;
+        }
+        Date now = new Date();
+        Date start = activity.getSignupStartTime();
+        Date end = activity.getSignupEndTime();
+        if (start != null && now.before(start)) {
+            return false;
+        }
+        if (end != null && now.after(end)) {
+            return false;
+        }
+        return true;
+    }
+
+    private Integer resolveDetailStatus(StoreOperationalActivity activity, Integer signupStatus) {
+        if (activity == null) {
+            return null;
+        }
+        Date now = new Date();
+        if (activity.getStatus() != null && activity.getStatus() == 7) {
+            return 0;
+        }
+        if (activity.getEndTime() != null && now.after(activity.getEndTime())) {
+            return 0;
+        }
+        Integer status = activity.getStatus();
+        if (status != null && (status == 0 || status == 1 || status == 3 || status == 4 || status == 6)) {
+            return 1;
+        }
+        if (signupStatus != null && signupStatus == 2
+                && activity.getStartTime() != null && now.before(activity.getStartTime())) {
+            return 2;
+        }
+        if (activity.getSignupEndTime() != null && now.after(activity.getSignupEndTime())) {
+            if (status != null && status == 5 && signupStatus != null && signupStatus == 2) {
+                return 3;
             }
-        } catch (Exception e) {
-            log.debug("获取当前用户失败,无法判断是否已报名", e);
+            return 1;
         }
+        if (signupStatus != null) {
+            if (signupStatus == 2) {
+                return 3;
+            }
+            if (signupStatus == 0) {
+                return 4;
+            }
+            if (signupStatus == 1) {
+                if (activity.getSignupEndTime() != null && now.before(activity.getSignupEndTime())) {
+                    return 5;
+                }
+                return 1;
+            }
+        }
+        return 5;
+    }
 
-        if (currentUserId == null) {
-            vo.setSignedUp(false);
-            return;
+    private Integer resolveCurrentUserSignupStatus(Integer activityId, Integer userId) {
+        if (userId == null || activityId == null) {
+            return null;
         }
-        Integer signupCount = signupMapper.countSignedUpByActivityAndUser(activity.getId(), currentUserId);
-        vo.setSignedUp(signupCount != null && signupCount > 0);
+        return signupMapper.selectLatestSignupStatus(activityId, userId);
     }
 
     private String resolveSignupStatusLabel(Integer signupStatus, Integer activityStatus) {
@@ -301,21 +380,56 @@ public class StoreOperationalActivityServiceImpl implements StoreOperationalActi
             return null;
         }
         if (signupStatus == 0) {
-            return "审核中";
+            return "报名未开始";
         }
         if (signupStatus == 1) {
-            return "报名已拒绝";
+            return "报名";
         }
         if (signupStatus == 2) {
-            if (activityStatus != null) {
-                if (activityStatus == 5) {
-                    return "活动已开始";
-                }
-                if (activityStatus == 7) {
-                    return "活动已结束";
-                }
+            return "报名已结束";
+        }
+        return null;
+    }
+
+    private Integer resolveMySignupStatus(StoreOperationalActivityMySignupVo item) {
+        /**
+         * 列表状态计算规则:
+         * 1 -> 活动已结束且无成果
+         * 2 -> 活动已结束且有成果
+         * 3 -> 活动进行中且有成果
+         * 4 -> 活动进行中且已报名无成果
+         * 5 -> 活动未开始且报名已拒绝
+         * 6 -> 活动未开始且报名成功
+         * 7 -> 报名审核中
+         */
+        if (item == null) {
+            return null;
+        }
+        Date now = new Date();
+        Integer activityStatus = item.getActivityStatus();
+        boolean hasAchievement = item.getHasAchievement() != null && item.getHasAchievement() == 1;
+        Integer auditStatus = item.getSignupAuditStatus();
+        Date startTime = item.getStartTime();
+        Date endTime = item.getEndTime();
+        boolean ended = (activityStatus != null && activityStatus == 7)
+                || (endTime != null && now.after(endTime));
+        if (ended) {
+            return hasAchievement ? 2 : 1;
+        }
+        if (activityStatus != null && activityStatus == 5) {
+            return hasAchievement ? 3 : 4;
+        }
+        if (activityStatus != null && activityStatus == 2) {
+            if (auditStatus != null && auditStatus == 1) {
+                return 5;
+            }
+            if (auditStatus != null && auditStatus == 2) {
+                return 6;
             }
-            return "报名成功";
+        }
+        if (auditStatus != null && auditStatus == 0
+                && startTime != null && now.before(startTime)) {
+            return 7;
         }
         return null;
     }