Kaynağa Gözat

fix codex抵扣升级抵扣金额;补偿车票天数

zoujiajian 1 ay önce
ebeveyn
işleme
dce84f5bec

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/mapper/GroupsRelationMapper.java

@@ -117,6 +117,11 @@ public interface GroupsRelationMapper extends BaseMapper<GroupsRelation> {
 
 	void incrExpiryTime(@Param("relationId") Long relationId, @Param("userId") Long userId);
 
+	List<GroupsRelation> selectCompensateRelations(@Param("goodsId") Long goodsId,
+			@Param("skuId") Long skuId,
+			@Param("isMirror") Boolean isMirror,
+			@Param("userId") Long userId);
+
 	List<UserTicketView> selectUserTickets(@Param("userIds") List<Long> userIds, @Param("goodsId") Long goodsId, @Param("skuId") Long skuId);
 
 	Integer selectCountByGoodsId(@Param("userIds") List<Long> userIdList, @Param("goodsId") Long goodsId, @Param("now") DateTime now);

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/statistics/IncrExpiryTimeRecordMapper.java

@@ -2,6 +2,8 @@ package com.cyksj.mapper.manage.statistics;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.IncrExpiryTimeRecord;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 /*
  *项目名: netflix
@@ -10,4 +12,7 @@ import com.cyksj.model.entity.IncrExpiryTimeRecord;
  *创建时间:2022/11/16 10:30
  */
 public interface IncrExpiryTimeRecordMapper extends BaseMapper<IncrExpiryTimeRecord> {
+
+	@Select("select id from incr_expiry_time_record where user_id = #{userId} and remark = #{remark} limit 1")
+	Long selectIdByUserIdAndRemark(@Param("userId") Long userId, @Param("remark") String remark);
 }

+ 50 - 0
netflix-dao/src/main/java/com/cyksj/model/request/TicketCompensationDaysReq.java

@@ -0,0 +1,50 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: TicketCompensationDaysReq
+ *创建者: JavaZou
+ *创建时间:2026/9/1 10:00
+ */
+@Getter
+@Setter
+public class TicketCompensationDaysReq {
+	/**
+	 * 商品id
+	 */
+	@NotNull(message = "商品id不为空")
+	private Long goodsId;
+
+	/**
+	 * 规格id,可选
+	 */
+	private Long skuId;
+
+	/**
+	 * 是否为镜像规格,可选;不传则包含全部规格
+	 */
+	private Boolean isMirror;
+
+	/**
+	 * 用户id,可选;不传则补偿该商品下所有有效车票用户
+	 */
+	private Long userId;
+
+	/**
+	 * 补偿天数
+	 */
+	@NotNull(message = "天数不为空")
+	private Integer days;
+
+	/**
+	 * 备注,同时作为补偿批次的去重标识
+	 */
+	@NotBlank(message = "备注不为空")
+	private String remark;
+}

+ 22 - 0
netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml

@@ -46,6 +46,28 @@
         update groups_relation set expiry_time = DATE_ADD(expiry_time,interval 1 day) where id = #{relationId} and user_id = #{userId}
     </update>
 
+    <select id="selectCompensateRelations" resultType="com.cyksj.model.entity.GroupsRelation">
+        select gr.*
+        from groups_relation gr
+        inner join groups_trips gt on gt.id = gr.groups_id
+        inner join goods_don_sku sku on sku.id = gt.sku_id
+        where sku.goods_id = #{goodsId}
+          and gt.status = 'validity'
+          and gr.status in ('validity', 'outside')
+          and gr.user_id != 0
+          and gr.expiry_time is not null
+        <if test="skuId != null">
+            and gt.sku_id = #{skuId}
+        </if>
+        <if test="isMirror != null">
+            and sku.is_mirror = #{isMirror}
+        </if>
+        <if test="userId != null">
+            and gr.user_id = #{userId}
+        </if>
+        order by gr.user_id, gr.expiry_time desc, gr.id desc
+    </select>
+
     <update id="updateUserVipSkusExpiryTime">
         update groups_relation set expiry_time = #{expiryTime},
         update_time = now()

+ 12 - 1
netflix-service/src/main/java/com/cyksj/service/codex/impl/CodexServiceImpl.java

@@ -800,8 +800,15 @@ public class CodexServiceImpl implements CodexService {
 	public BigDecimal getDeductMoneyFromTicket(Long relationId, List<Long> userIdList, Long skuId, DateTime now, Date relationStartTime, Date relationExpiryTime) {
 		OrderDon oriOrderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getRelationId, relationId).in(OrderDon::getUserId, userIdList).notIn(OrderDon::getStatus, Constant.noOrderAllStatus).eq(OrderDon::getOrderType, 1).orderByDesc(OrderDon::getId).last("limit 1"));
 		LambdaQueryWrapper<OrderDon> queryWrapper = Wrappers.lambdaQuery(OrderDon.class);
+		Long remainDays = DateUtil.betweenDay(now, relationExpiryTime, false) + 1;
+		int deductMonth = Math.max(1, BigDecimal.valueOf(remainDays).divide(BigDecimal.valueOf(30), 0, RoundingMode.UP).intValue());
+		Date deductStartTime = DateUtil.offsetMonth(now, -deductMonth);
+		Date cutoffTime = deductStartTime;
 		if (oriOrderDon != null) {
-			queryWrapper.ge(OrderDon::getCreatedTime, oriOrderDon.getCreatedTime());
+			cutoffTime = DateUtil.compare(oriOrderDon.getCreatedTime(), deductStartTime) > 0 ? oriOrderDon.getCreatedTime() : deductStartTime;
+			queryWrapper.ge(OrderDon::getCreatedTime, cutoffTime);
+		} else {
+			queryWrapper.ge(OrderDon::getCreatedTime, deductStartTime);
 		}
 		List<OrderDon> orderDonList = orderDonMapper.selectList(queryWrapper
 				.eq(OrderDon::getRelationId, relationId)
@@ -812,6 +819,7 @@ public class CodexServiceImpl implements CodexService {
 		//部分退款
 		if (orderDonList.isEmpty()) {
 			orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class)
+					.ge(OrderDon::getCreatedTime, cutoffTime)
 					.eq(OrderDon::getRelationId, relationId)
 					.in(OrderDon::getUserId, userIdList)
 					.eq(OrderDon::getSkuId, skuId)
@@ -861,6 +869,9 @@ public class CodexServiceImpl implements CodexService {
 				}
 			}
 		}
+		if (totalDays == 0) {
+			return BigDecimal.ZERO;
+		}
 		Long thisDeductBetweenDay = DateUtil.betweenDay(now, relationExpiryTime, false) + 1;
 		BigDecimal single = totalOrderMoney.divide(BigDecimal.valueOf(totalDays), 0, RoundingMode.HALF_UP);
 		//该车票抵扣金额

+ 54 - 8
netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java

@@ -30,6 +30,7 @@ import com.cyksj.model.excel.ExcelSheetAndData;
 import com.cyksj.model.request.BatchServiceReq;
 import com.cyksj.model.request.RedBookReq;
 import com.cyksj.model.request.RenewTimeUpReq;
+import com.cyksj.model.request.TicketCompensationDaysReq;
 import com.cyksj.model.views.*;
 import com.cyksj.service.claude.ClaudeCodeService;
 import com.cyksj.service.codex.CodexService;
@@ -226,14 +227,60 @@ public class StatisticsDataController {
 		if (relation.getExpiryTime() == null) {
 			throw BusinessRuntimeException.getInstance("车票已过期");
 		}
+		addRelationExpiryDays(relation, renewTimeUpReq.getDays(), renewTimeUpReq.getRemark(), adminId, false);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("增加车票有效期成功");
+	}
+
+	/**
+	 * 按商品补偿车票天数
+	 */
+	@PutMapping("/ticket/compensation/days")
+	@Transactional(rollbackFor = Throwable.class)
+	@SaCheckPermission("validAccount:addTime")
+	@Log(module = "有效子账号管理/补偿车票天数", businessType = BusinessType.PUT, isSaveRequestData = true)
+	public Result<String> compensateTicketDays(@RequestBody @Validated TicketCompensationDaysReq req) {
+		if (req.getDays() == null || req.getDays() <= 0) {
+			throw BusinessRuntimeException.getInstance("补偿天数必须大于0");
+		}
+		long adminId = StpUtil.getLoginIdAsLong();
+		List<GroupsRelation> relations = groupsRelationMapper.selectCompensateRelations(
+				req.getGoodsId(), req.getSkuId(), req.getIsMirror(), req.getUserId());
+		if (CollUtil.isEmpty(relations)) {
+			throw BusinessRuntimeException.getInstance("未找到可补偿的有效车票");
+		}
+		String remark = StrUtil.trim(req.getRemark());
+		Map<Long, GroupsRelation> relationByUser = new LinkedHashMap<>();
+		for (GroupsRelation relation : relations) {
+			relationByUser.putIfAbsent(relation.getUserId(), relation);
+		}
+		int compensatedUsers = 0;
+		int skippedUsers = 0;
+		for (GroupsRelation relation : relationByUser.values()) {
+			Long userId = relation.getUserId();
+			Long recordId = incrExpiryTimeRecordMapper.selectIdByUserIdAndRemark(userId, remark);
+			if (recordId != null) {
+				skippedUsers++;
+				continue;
+			}
+			addRelationExpiryDays(relation, req.getDays(), remark, adminId, true);
+			compensatedUsers++;
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult(String.format(
+				"补偿车票有效期完成,成功补偿%s个用户,跳过%s个已补偿用户", compensatedUsers, skippedUsers));
+	}
+
+	private void addRelationExpiryDays(GroupsRelation relation, Integer days, String remark, long adminId, boolean compensation) {
 		IncrExpiryTimeRecord incrExpiryTimeRecord = new IncrExpiryTimeRecord();
 		incrExpiryTimeRecord.setExpiryTimeInitial(relation.getExpiryTime());
-		incrExpiryTimeRecord.setIncrExpiryDays(renewTimeUpReq.getDays());
-		incrExpiryTimeRecord.setRemark(renewTimeUpReq.getRemark());
-		relation.setExpiryTime(DateUtil.offsetDay(relation.getExpiryTime(), renewTimeUpReq.getDays()));
+		incrExpiryTimeRecord.setIncrExpiryDays(days);
+		incrExpiryTimeRecord.setRemark(remark);
+		relation.setExpiryTime(DateUtil.offsetDay(relation.getExpiryTime(), days));
 		if (relation.getReservedDays() != null && relation.getReservedDays() > 0) {
 			relation.setReservedDays(relation.getReservedDays() + incrExpiryTimeRecord.getIncrExpiryDays());
 		}
+		if (compensation) {
+			relation.setCompensationDays(Optional.ofNullable(relation.getCompensationDays()).orElse(0) + days);
+		}
 		groupsRelationMapper.updateById(relation);
 
 		//扣除镜像过期时间
@@ -241,14 +288,14 @@ public class StatisticsDataController {
 				.eq(ChatgptUser::getRelationId, relation.getId())
 				.last("limit 1"));
 		if (chatgptUser != null) {
-			chatgptUser.setExpireTime(DateUtil.offsetDay(chatgptUser.getExpireTime(), renewTimeUpReq.getDays()));
+			chatgptUser.setExpireTime(DateUtil.offsetDay(chatgptUser.getExpireTime(), days));
 			chatgptUserMapper.updateById(chatgptUser);
 		} else {
 			MidjourneyUser midjourneyUser = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class)
 					.eq(MidjourneyUser::getRelationId, relation.getId())
 					.last("limit 1"));
 			if (midjourneyUser != null) {
-				midjourneyUser.setExpireTime(DateUtil.offsetDay(midjourneyUser.getExpireTime(), renewTimeUpReq.getDays()));
+				midjourneyUser.setExpireTime(DateUtil.offsetDay(midjourneyUser.getExpireTime(), days));
 				midjourneyUserMapper.updateById(midjourneyUser);
 			}
 		}
@@ -267,19 +314,18 @@ public class StatisticsDataController {
 			GroupsTrips groupsTrips = groupsMapper.selectById(groupsId);
 			Long skuId = groupsTrips.getSkuId();
 			GoodsDonSku sku = goodsDonSkuMapper.selectById(skuId);
-			if (sku.getGoodsId() == Constant.CODEX_GOODS_ID) {
+			if (Objects.equals(sku.getGoodsId(), Constant.CODEX_GOODS_ID)) {
 				CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, relation.getId()).last("limit 1"));
 				if (codexUser != null) {
 					codexService.updateCodexUserPackageInfo(codexUser, relation.getExpiryTime(), null);
 				}
-			} else if (sku.getGoodsId() == Constant.CLAUDE_CODE_GOODS_ID) {
+			} else if (Objects.equals(sku.getGoodsId(), Constant.CLAUDE_CODE_GOODS_ID)) {
 				ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).eq(ClaudeCodeUser::getRelationId, relation.getId()).last("limit 1"));
 				if (claudeCodeUser != null) {
 					claudeCodeService.updateClaudeCodeUserPackageInfo(claudeCodeUser, relation.getExpiryTime(), null);
 				}
 			}
 		}
-		return GatewayResponse.SUCCESS.newBuilder().toResult("增加车票有效期成功");
 	}
 
 	/**