|
@@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
import cn.hutool.json.JSONUtil;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
import com.fs.app.facade.LiveFacadeService;
|
|
import com.fs.app.facade.LiveFacadeService;
|
|
|
import com.fs.common.constant.LiveKeysConstant;
|
|
import com.fs.common.constant.LiveKeysConstant;
|
|
|
import com.fs.common.core.controller.BaseController;
|
|
import com.fs.common.core.controller.BaseController;
|
|
@@ -12,6 +14,12 @@ import com.fs.common.core.page.PageRequest;
|
|
|
import com.fs.common.core.page.TableDataInfo;
|
|
import com.fs.common.core.page.TableDataInfo;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
import com.fs.common.utils.StringUtils;
|
|
|
|
|
+import com.fs.company.domain.CompanyUser;
|
|
|
|
|
+import com.fs.company.service.ICompanyUserService;
|
|
|
|
|
+import com.fs.his.domain.FsUser;
|
|
|
|
|
+import com.fs.his.param.WxSendRedPacketParam;
|
|
|
|
|
+import com.fs.his.service.IFsStorePaymentService;
|
|
|
|
|
+import com.fs.his.service.IFsUserService;
|
|
|
import com.fs.live.domain.*;
|
|
import com.fs.live.domain.*;
|
|
|
import com.fs.framework.aspectj.lock.DistributeLock;
|
|
import com.fs.framework.aspectj.lock.DistributeLock;
|
|
|
import com.fs.live.param.CouponPO;
|
|
import com.fs.live.param.CouponPO;
|
|
@@ -25,6 +33,7 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.ZoneId;
|
|
import java.time.ZoneId;
|
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.time.temporal.ChronoUnit;
|
|
@@ -74,10 +83,13 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
private ILiveCouponIssueService liveCouponIssueService;
|
|
private ILiveCouponIssueService liveCouponIssueService;
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
- private com.fs.his.service.IFsUserService fsUserService;
|
|
|
|
|
|
|
+ private IFsUserService fsUserService;
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
- private com.fs.his.service.IFsStorePaymentService paymentService;
|
|
|
|
|
|
|
+ private IFsStorePaymentService paymentService;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private ICompanyUserService companyUserService;
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private ILiveRewardRecordService liveRewardRecordService;
|
|
private ILiveRewardRecordService liveRewardRecordService;
|
|
@@ -308,6 +320,7 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 直播签到领取:写 live_sign_record 防重,再按 rewards 发奖并绑定留存。
|
|
* 直播签到领取:写 live_sign_record 防重,再按 rewards 发奖并绑定留存。
|
|
|
|
|
+ * 防重按「本场开播」判定(createTime >= live.startTime),结束后重开可再签。
|
|
|
* 金额红包成功时返回 package 等字段,供 App 跳转微信确认收款。
|
|
* 金额红包成功时返回 package 等字段,供 App 跳转微信确认收款。
|
|
|
*/
|
|
*/
|
|
|
@Override
|
|
@Override
|
|
@@ -319,18 +332,23 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
Long liveId = sign.getLiveId();
|
|
Long liveId = sign.getLiveId();
|
|
|
String signNo = String.valueOf(sign.getSignNo());
|
|
String signNo = String.valueOf(sign.getSignNo());
|
|
|
|
|
|
|
|
- // 防重复签到
|
|
|
|
|
|
|
+ Live live = liveService.selectLiveByLiveId(liveId);
|
|
|
|
|
+ if (live == null) {
|
|
|
|
|
+ return R.error("直播间不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 防重复签到:仅拦截本场(开播后)已签记录,结束后重开可再签
|
|
|
LiveSignRecord existQuery = new LiveSignRecord();
|
|
LiveSignRecord existQuery = new LiveSignRecord();
|
|
|
existQuery.setLiveId(liveId);
|
|
existQuery.setLiveId(liveId);
|
|
|
existQuery.setUserId(userId);
|
|
existQuery.setUserId(userId);
|
|
|
existQuery.setSignNo(signNo);
|
|
existQuery.setSignNo(signNo);
|
|
|
List<LiveSignRecord> exists = liveSignRecordService.selectLiveSignRecordList(existQuery);
|
|
List<LiveSignRecord> exists = liveSignRecordService.selectLiveSignRecordList(existQuery);
|
|
|
- if (CollUtil.isNotEmpty(exists)) {
|
|
|
|
|
|
|
+ if (hasSignedInCurrentSession(exists, live.getStartTime())) {
|
|
|
return R.error("您已完成该次签到");
|
|
return R.error("您已完成该次签到");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 从触发缓存取奖励留存;没有则从已执行签到任务解析
|
|
// 从触发缓存取奖励留存;没有则从已执行签到任务解析
|
|
|
- List<com.alibaba.fastjson.JSONObject> rewards = resolveSignRewards(liveId, signNo, sign.getTaskId());
|
|
|
|
|
|
|
+ List<JSONObject> rewards = resolveSignRewards(liveId, signNo, sign.getTaskId());
|
|
|
if (CollUtil.isEmpty(rewards)) {
|
|
if (CollUtil.isEmpty(rewards)) {
|
|
|
return R.error("签到任务尚未开启或奖励配置不存在");
|
|
return R.error("签到任务尚未开启或奖励配置不存在");
|
|
|
}
|
|
}
|
|
@@ -344,7 +362,7 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
liveSignRecordService.save(record);
|
|
liveSignRecordService.save(record);
|
|
|
|
|
|
|
|
List<Map<String, Object>> claimResults = new ArrayList<>();
|
|
List<Map<String, Object>> claimResults = new ArrayList<>();
|
|
|
- for (com.alibaba.fastjson.JSONObject item : rewards) {
|
|
|
|
|
|
|
+ for (JSONObject item : rewards) {
|
|
|
Long rewardType = item.getLong("rewardType");
|
|
Long rewardType = item.getLong("rewardType");
|
|
|
Long opLogId = item.getLong("opLogId");
|
|
Long opLogId = item.getLong("opLogId");
|
|
|
Map<String, Object> one = new HashMap<>();
|
|
Map<String, Object> one = new HashMap<>();
|
|
@@ -422,9 +440,10 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
/**
|
|
/**
|
|
|
* 签到金额红包:发起微信商家转账(金额精确到 0.1 元)。
|
|
* 签到金额红包:发起微信商家转账(金额精确到 0.1 元)。
|
|
|
* 成功时返回 package/mchId/appId/orderCode,供 App 跳转微信确认收款。
|
|
* 成功时返回 package/mchId/appId/orderCode,供 App 跳转微信确认收款。
|
|
|
|
|
+ * 扣款公司取用户绑定销售(bindCompanyUserId)所属公司,而非直播间公司。
|
|
|
*/
|
|
*/
|
|
|
- private R claimSignCashRed(Long liveId, Long userId, com.alibaba.fastjson.JSONObject item, SignPO sign) {
|
|
|
|
|
- java.math.BigDecimal amount = item.getBigDecimal("amount");
|
|
|
|
|
|
|
+ private R claimSignCashRed(Long liveId, Long userId, JSONObject item, SignPO sign) {
|
|
|
|
|
+ BigDecimal amount = item.getBigDecimal("amount");
|
|
|
if (amount == null || amount.compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
|
if (amount == null || amount.compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
|
|
return R.error("金额红包配置无效");
|
|
return R.error("金额红包配置无效");
|
|
|
}
|
|
}
|
|
@@ -432,13 +451,20 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
if (live == null) {
|
|
if (live == null) {
|
|
|
return R.error("直播间不存在");
|
|
return R.error("直播间不存在");
|
|
|
}
|
|
}
|
|
|
- com.fs.his.domain.FsUser user = fsUserService.selectFsUserById(userId);
|
|
|
|
|
|
|
+ FsUser user = fsUserService.selectFsUserById(userId);
|
|
|
if (user == null) {
|
|
if (user == null) {
|
|
|
return R.error("用户不存在");
|
|
return R.error("用户不存在");
|
|
|
}
|
|
}
|
|
|
- if (live.getCompanyId() == null) {
|
|
|
|
|
- return R.error("直播间未绑定公司,无法发放金额红包");
|
|
|
|
|
|
|
+ // 通过用户绑定销售ID查询销售所属公司,作为红包扣款公司
|
|
|
|
|
+ Long bindCompanyUserId = user.getBindCompanyUserId();
|
|
|
|
|
+ if (bindCompanyUserId == null) {
|
|
|
|
|
+ return R.error("用户未绑定销售,无法发放金额红包");
|
|
|
|
|
+ }
|
|
|
|
|
+ CompanyUser companyUser = companyUserService.selectCompanyUserById(bindCompanyUserId);
|
|
|
|
|
+ if (companyUser == null || companyUser.getCompanyId() == null) {
|
|
|
|
|
+ return R.error("绑定销售不存在或未归属公司,无法发放金额红包");
|
|
|
}
|
|
}
|
|
|
|
|
+ Long companyId = companyUser.getCompanyId();
|
|
|
String openId = null;
|
|
String openId = null;
|
|
|
if (sign != null && sign.getSource() != null && sign.getSource() == 2
|
|
if (sign != null && sign.getSource() != null && sign.getSource() == 2
|
|
|
&& StringUtils.isNotEmpty(user.getCourseMaOpenId())) {
|
|
&& StringUtils.isNotEmpty(user.getCourseMaOpenId())) {
|
|
@@ -451,16 +477,16 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
if (StringUtils.isEmpty(openId)) {
|
|
if (StringUtils.isEmpty(openId)) {
|
|
|
return R.error("请使用微信登录后再领取金额红包");
|
|
return R.error("请使用微信登录后再领取金额红包");
|
|
|
}
|
|
}
|
|
|
- com.fs.his.param.WxSendRedPacketParam packetParam = new com.fs.his.param.WxSendRedPacketParam();
|
|
|
|
|
|
|
+ WxSendRedPacketParam packetParam = new com.fs.his.param.WxSendRedPacketParam();
|
|
|
packetParam.setOpenId(openId);
|
|
packetParam.setOpenId(openId);
|
|
|
packetParam.setAmount(amount.setScale(1, java.math.RoundingMode.HALF_UP));
|
|
packetParam.setAmount(amount.setScale(1, java.math.RoundingMode.HALF_UP));
|
|
|
- packetParam.setCompanyId(live.getCompanyId());
|
|
|
|
|
|
|
+ packetParam.setCompanyId(companyId);
|
|
|
packetParam.setSource(sign != null && sign.getSource() != null ? sign.getSource() : 1);
|
|
packetParam.setSource(sign != null && sign.getSource() != null ? sign.getSource() : 1);
|
|
|
packetParam.setAppId(sign != null ? sign.getAppId() : null);
|
|
packetParam.setAppId(sign != null ? sign.getAppId() : null);
|
|
|
packetParam.setRedPacketMode(1);
|
|
packetParam.setRedPacketMode(1);
|
|
|
packetParam.setUser(user);
|
|
packetParam.setUser(user);
|
|
|
try {
|
|
try {
|
|
|
- R sendResult = paymentService.sendRedPacket(packetParam);
|
|
|
|
|
|
|
+ R sendResult = paymentService.sendAppRedPacket(packetParam);
|
|
|
if (!Integer.valueOf(200).equals(sendResult.get("code"))) {
|
|
if (!Integer.valueOf(200).equals(sendResult.get("code"))) {
|
|
|
return sendResult;
|
|
return sendResult;
|
|
|
}
|
|
}
|
|
@@ -473,7 +499,7 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
rewardRecord.setNum(amount);
|
|
rewardRecord.setNum(amount);
|
|
|
rewardRecord.setIncomeType(1L);
|
|
rewardRecord.setIncomeType(1L);
|
|
|
rewardRecord.setSourceType(7L);
|
|
rewardRecord.setSourceType(7L);
|
|
|
- rewardRecord.setSourceId(live.getCompanyId() != null ? live.getCompanyId() : 0L);
|
|
|
|
|
|
|
+ rewardRecord.setSourceId(companyId);
|
|
|
rewardRecord.setCreateBy(String.valueOf(userId));
|
|
rewardRecord.setCreateBy(String.valueOf(userId));
|
|
|
liveRewardRecordService.insertLiveRewardRecord(rewardRecord);
|
|
liveRewardRecordService.insertLiveRewardRecord(rewardRecord);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
@@ -489,14 +515,14 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
/**
|
|
/**
|
|
|
* 解析本次签到可领取奖励:优先读触发后 Redis 缓存(含 opLogId),否则回落已执行签到任务 content.rewards。
|
|
* 解析本次签到可领取奖励:优先读触发后 Redis 缓存(含 opLogId),否则回落已执行签到任务 content.rewards。
|
|
|
*/
|
|
*/
|
|
|
- private List<com.alibaba.fastjson.JSONObject> resolveSignRewards(Long liveId, String signNo, Long taskId) {
|
|
|
|
|
- List<com.alibaba.fastjson.JSONObject> rewards = new ArrayList<>();
|
|
|
|
|
|
|
+ private List<JSONObject> resolveSignRewards(Long liveId, String signNo, Long taskId) {
|
|
|
|
|
+ List<JSONObject> rewards = new ArrayList<>();
|
|
|
try {
|
|
try {
|
|
|
Object cache = redisCache.getCacheObject(String.format(LiveKeysConstant.LIVE_SIGN_OP_LOGS, liveId, signNo));
|
|
Object cache = redisCache.getCacheObject(String.format(LiveKeysConstant.LIVE_SIGN_OP_LOGS, liveId, signNo));
|
|
|
if (cache != null) {
|
|
if (cache != null) {
|
|
|
- com.alibaba.fastjson.JSONObject data = JSON.parseObject(String.valueOf(cache));
|
|
|
|
|
|
|
+ JSONObject data = JSON.parseObject(String.valueOf(cache));
|
|
|
if (data != null && data.getJSONArray("rewards") != null) {
|
|
if (data != null && data.getJSONArray("rewards") != null) {
|
|
|
- com.alibaba.fastjson.JSONArray arr = data.getJSONArray("rewards");
|
|
|
|
|
|
|
+ JSONArray arr = data.getJSONArray("rewards");
|
|
|
for (int i = 0; i < arr.size(); i++) {
|
|
for (int i = 0; i < arr.size(); i++) {
|
|
|
rewards.add(arr.getJSONObject(i));
|
|
rewards.add(arr.getJSONObject(i));
|
|
|
}
|
|
}
|
|
@@ -525,7 +551,7 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
- com.alibaba.fastjson.JSONObject c = JSON.parseObject(t.getContent());
|
|
|
|
|
|
|
+ JSONObject c = JSON.parseObject(t.getContent());
|
|
|
if (c != null && signNo.equals(String.valueOf(c.get("signNo")))) {
|
|
if (c != null && signNo.equals(String.valueOf(c.get("signNo")))) {
|
|
|
task = t;
|
|
task = t;
|
|
|
break;
|
|
break;
|
|
@@ -539,14 +565,14 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
return rewards;
|
|
return rewards;
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
- com.alibaba.fastjson.JSONObject content = JSON.parseObject(task.getContent());
|
|
|
|
|
- com.alibaba.fastjson.JSONArray arr = content.getJSONArray("rewards");
|
|
|
|
|
|
|
+ JSONObject content = JSON.parseObject(task.getContent());
|
|
|
|
|
+ JSONArray arr = content.getJSONArray("rewards");
|
|
|
if (arr != null) {
|
|
if (arr != null) {
|
|
|
for (int i = 0; i < arr.size(); i++) {
|
|
for (int i = 0; i < arr.size(); i++) {
|
|
|
rewards.add(arr.getJSONObject(i));
|
|
rewards.add(arr.getJSONObject(i));
|
|
|
}
|
|
}
|
|
|
} else if (content.getLong("rewardType") != null) {
|
|
} else if (content.getLong("rewardType") != null) {
|
|
|
- com.alibaba.fastjson.JSONObject single = new com.alibaba.fastjson.JSONObject();
|
|
|
|
|
|
|
+ JSONObject single = new JSONObject();
|
|
|
single.put("rewardType", content.getLong("rewardType"));
|
|
single.put("rewardType", content.getLong("rewardType"));
|
|
|
single.put("rewardId", content.getLong("rewardId"));
|
|
single.put("rewardId", content.getLong("rewardId"));
|
|
|
single.put("reward", content.get("reward"));
|
|
single.put("reward", content.get("reward"));
|
|
@@ -558,6 +584,26 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
|
|
|
return rewards;
|
|
return rewards;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 是否已在当前开播场次签过该次签到。
|
|
|
|
|
+ * 以 live.startTime 为场次边界:结束后重开(startTime 更新)可再签。
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean hasSignedInCurrentSession(List<LiveSignRecord> records, LocalDateTime startTime) {
|
|
|
|
|
+ if (CollUtil.isEmpty(records)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (startTime == null) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return records.stream().anyMatch(r -> {
|
|
|
|
|
+ if (r.getCreateTime() == null) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ LocalDateTime recordLdt = r.getCreateTime().toInstant().atZone(ZONE_ID).toLocalDateTime();
|
|
|
|
|
+ return !recordLdt.isBefore(startTime);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private Long resolveLiveDuration(Long liveId) {
|
|
private Long resolveLiveDuration(Long liveId) {
|
|
|
if (liveId == null) {
|
|
if (liveId == null) {
|
|
|
return 0L;
|
|
return 0L;
|