初始化天卡开放平台后端项目

包含完整功能模块:
- 设备激活(直销激活码模式 + 代销免激活码模式)
- 微信支付 + 天卡订单
- 天卡池管理 + 异步橘视厅SDK激活
- 第三方合作伙伴接口(代销模式)
  - 签名鉴权(appKey/appSecret + nonce防重放)
  - 天卡消耗(事务 + 幂等 + 并发控制)
  - 媒资接口(三级授权过滤)
  - 审计日志
- 定价策略(3层:门店 > 渠道 > 平台)
- 分账(微信分账 + 分润规则)
- Docker部署(含Redis容器)
This commit is contained in:
zhaogd
2026-07-26 12:34:13 +08:00
commit 30198c7191
349 changed files with 28837 additions and 0 deletions
@@ -0,0 +1,65 @@
package io.as.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.as.common.utils.PageUtils;
import io.as.common.utils.Query;
import io.as.modules.openapi.dao.ContentPackageDao;
import io.as.modules.openapi.entity.ContentPackageEntity;
import io.as.modules.openapi.service.ContentPackageService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* 开放API内容包授权
*/
@Service("contentPackageService")
public class ContentPackageServiceImpl extends ServiceImpl<ContentPackageDao, ContentPackageEntity> implements ContentPackageService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
String name = (String) params.get("name");
String status = (String) params.get("status");
IPage<ContentPackageEntity> page = this.page(
new Query<ContentPackageEntity>().getPage(params),
new QueryWrapper<ContentPackageEntity>()
.like(StringUtils.isNotBlank(name), "name", name)
.eq(StringUtils.isNotBlank(status), "status", status)
.eq("delete_flag", "UNDEL")
.orderByDesc("created_at")
);
return new PageUtils(page);
}
@Override
public ContentPackageEntity getByPartnerId(String partnerId) {
return this.getOne(
new QueryWrapper<ContentPackageEntity>()
.eq("partner_id", partnerId)
.eq("status", "ACTIVE")
.eq("delete_flag", "UNDEL")
.last("LIMIT 1")
);
}
@Override
public void saveOrUpdatePackage(ContentPackageEntity entity) {
ContentPackageEntity exist = this.getOne(
new QueryWrapper<ContentPackageEntity>()
.eq("partner_id", entity.getPartnerId())
.eq("delete_flag", "UNDEL")
.last("LIMIT 1")
);
if (exist != null) {
entity.setId(exist.getId());
this.updateById(entity);
} else {
this.save(entity);
}
}
}
@@ -0,0 +1,53 @@
package io.as.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.as.common.utils.PageUtils;
import io.as.common.utils.Query;
import io.as.modules.openapi.dao.OpenApiCallLogDao;
import io.as.modules.openapi.entity.OpenApiCallLogEntity;
import io.as.modules.openapi.service.OpenApiCallLogService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Map;
/**
* 开放API调用日志
*/
@Service("openApiCallLogService")
public class OpenApiCallLogServiceImpl extends ServiceImpl<OpenApiCallLogDao, OpenApiCallLogEntity> implements OpenApiCallLogService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
String partnerId = (String) params.get("partnerId");
String api = (String) params.get("api");
IPage<OpenApiCallLogEntity> page = this.page(
new Query<OpenApiCallLogEntity>().getPage(params),
new QueryWrapper<OpenApiCallLogEntity>()
.eq(StringUtils.isNotBlank(partnerId), "partner_id", partnerId)
.like(StringUtils.isNotBlank(api), "api", api)
.orderByDesc("created_at")
);
return new PageUtils(page);
}
@Override
public void saveLog(String partnerId, String appKey, String api, String method, String params, Integer resultCode, String ip, Integer costMs) {
OpenApiCallLogEntity entity = new OpenApiCallLogEntity();
entity.setPartnerId(partnerId);
entity.setAppKey(appKey);
entity.setApi(api);
entity.setHttpMethod(method);
entity.setParams(params);
entity.setResultCode(resultCode);
entity.setIp(ip);
entity.setCostMs(costMs);
entity.setCreatedAt(new Date());
this.save(entity);
}
}
@@ -0,0 +1,49 @@
package io.as.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.as.common.utils.PageUtils;
import io.as.common.utils.Query;
import io.as.modules.openapi.dao.OpenApiPartnerDao;
import io.as.modules.openapi.entity.OpenApiPartnerEntity;
import io.as.modules.openapi.service.OpenApiPartnerService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* 开放API合作伙伴
*/
@Service("openApiPartnerService")
public class OpenApiPartnerServiceImpl extends ServiceImpl<OpenApiPartnerDao, OpenApiPartnerEntity> implements OpenApiPartnerService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
String name = (String) params.get("name");
String partnerId = (String) params.get("partnerId");
String status = (String) params.get("status");
IPage<OpenApiPartnerEntity> page = this.page(
new Query<OpenApiPartnerEntity>().getPage(params),
new QueryWrapper<OpenApiPartnerEntity>()
.like(StringUtils.isNotBlank(name), "name", name)
.like(StringUtils.isNotBlank(partnerId), "partner_id", partnerId)
.eq(StringUtils.isNotBlank(status), "status", status)
.eq("delete_flag", "UNDEL")
.orderByDesc("created_at")
);
return new PageUtils(page);
}
@Override
public OpenApiPartnerEntity getByAppKey(String appKey) {
return this.getOne(
new QueryWrapper<OpenApiPartnerEntity>()
.eq("app_key", appKey)
.eq("delete_flag", "UNDEL")
);
}
}
@@ -0,0 +1,442 @@
package io.as.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.as.common.exception.ASException;
import io.as.common.utils.PageUtils;
import io.as.common.utils.Query;
import io.as.common.utils.RedisUtils;
import io.as.modules.daycard.constants.DayCardConstants;
import io.as.modules.daycard.dao.DayCardPoolDao;
import io.as.modules.daycard.entity.DayCardPoolEntity;
import io.as.modules.device.dao.DeviceDao;
import io.as.modules.device.entity.DeviceEntity;
import io.as.modules.device.service.DeviceService;
import io.as.modules.jst.service.JstActivateCardResult;
import io.as.modules.jst.service.JushitingApiService;
import io.as.modules.openapi.dao.PartnerCardPoolDao;
import io.as.modules.openapi.entity.PartnerCardPoolEntity;
import io.as.modules.openapi.service.PartnerCardPoolService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* 合作伙伴天卡池
*/
@Service("partnerCardPoolService")
public class PartnerCardPoolServiceImpl extends ServiceImpl<PartnerCardPoolDao, PartnerCardPoolEntity> implements PartnerCardPoolService {
private static final Logger logger = LoggerFactory.getLogger(PartnerCardPoolServiceImpl.class);
@Autowired
private DayCardPoolDao dayCardPoolDao;
@Autowired
private DeviceDao deviceDao;
@Autowired
private DeviceService deviceService;
@Autowired
private JushitingApiService jushitingApiService;
@Autowired
@Qualifier("thirdPartyActivateExecutor")
private TaskExecutor activateExecutor;
@Autowired
private RedisUtils redisUtils;
@Override
public PageUtils queryPage(Map<String, Object> params) {
String partnerId = (String) params.get("partnerId");
String status = (String) params.get("status");
String purchaseBatch = (String) params.get("purchaseBatch");
IPage<PartnerCardPoolEntity> page = this.page(
new Query<PartnerCardPoolEntity>().getPage(params),
new QueryWrapper<PartnerCardPoolEntity>()
.eq(StringUtils.isNotBlank(partnerId), "partner_id", partnerId)
.eq(StringUtils.isNotBlank(status), "status", status)
.eq(StringUtils.isNotBlank(purchaseBatch), "purchase_batch", purchaseBatch)
.eq("delete_flag", "UNDEL")
.orderByDesc("created_at")
);
return new PageUtils(page);
}
@Override
public int getAvailableCount(String partnerId) {
return Math.toIntExact(this.count(
new QueryWrapper<PartnerCardPoolEntity>()
.eq("partner_id", partnerId)
.eq("status", "AVAILABLE")
.eq("delete_flag", "UNDEL")
));
}
@Override
public List<PartnerCardPoolEntity> getAvailableCards(String partnerId, int limit) {
return this.list(
new QueryWrapper<PartnerCardPoolEntity>()
.eq("partner_id", partnerId)
.eq("status", "AVAILABLE")
.eq("delete_flag", "UNDEL")
.orderByAsc("id")
.last("LIMIT " + limit)
);
}
@Override
public PartnerCardPoolEntity getByPoolId(Long poolId) {
return this.getOne(
new QueryWrapper<PartnerCardPoolEntity>()
.eq("pool_id", poolId)
.last("LIMIT 1")
);
}
@Override
public void saveBatchCards(String partnerId, String purchaseBatch, List<PartnerCardPoolEntity> cards) {
this.saveBatch(cards);
}
// ==================== 采购 ====================
@Override
public int purchase(String partnerId, int quantity) {
if (quantity <= 0) {
throw new ASException("采购数量必须大于0", 400);
}
// 1. 从平台天卡池取 quantity 张 UNUSED 的卡(SKIP LOCKED 防阻塞)
List<DayCardPoolEntity> cards = dayCardPoolDao.selectList(
new QueryWrapper<DayCardPoolEntity>()
.eq("status", DayCardConstants.POOL_STATUS_UNUSED)
.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
.last("LIMIT " + quantity + " FOR UPDATE SKIP LOCKED")
);
if (cards.isEmpty()) {
throw new ASException("平台天卡池库存不足", 400);
}
String purchaseBatch = "PUR" + System.currentTimeMillis();
Date now = new Date();
for (DayCardPoolEntity card : cards) {
// 2. 平台卡状态 UNUSED -> RESERVED(划拨给partner)
int updated = dayCardPoolDao.update(null, new UpdateWrapper<DayCardPoolEntity>()
.eq("id", card.getId())
.eq("status", DayCardConstants.POOL_STATUS_UNUSED)
.set("status", DayCardConstants.POOL_STATUS_RESERVED)
.set("updated_at", now)
);
if (updated == 0) {
continue; // 被其他线程抢走,跳过
}
// 3. 记录到 partner 池子
PartnerCardPoolEntity pcp = new PartnerCardPoolEntity();
pcp.setPartnerId(partnerId);
pcp.setPoolId(card.getId());
pcp.setCardCode(card.getCardCode());
pcp.setThirdCardNo(card.getThirdCardNo());
pcp.setStatus("AVAILABLE");
pcp.setPurchaseBatch(purchaseBatch);
pcp.setDeleteFlag("UNDEL");
pcp.setCreatedAt(now);
pcp.setUpdatedAt(now);
this.save(pcp);
}
logger.info("合作伙伴采购天卡: partnerId={}, quantity={}, batch={}", partnerId, cards.size(), purchaseBatch);
return cards.size();
}
// ==================== 消耗(代销模式) ====================
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> consume(String partnerId, String mac, String wifiMac, String deviceUid, String deviceSn, Long channelOrgId) {
if (StringUtils.isBlank(mac) && StringUtils.isBlank(wifiMac)
&& StringUtils.isBlank(deviceUid) && StringUtils.isBlank(deviceSn)) {
throw new ASException("mac/wifiMac/deviceUid/deviceSn 至少传一个", 400);
}
// 1. Redis防重锁:按设备标识加锁,防止并发重复消耗
String lockKey = DayCardConstants.REDIS_PARTNER_CONSUME_LOCK_PREFIX
+ (StringUtils.isNotBlank(mac) ? mac
: StringUtils.isNotBlank(wifiMac) ? wifiMac
: StringUtils.isNotBlank(deviceUid) ? deviceUid : deviceSn);
boolean locked = redisUtils.setIfAbsent(lockKey, "1", 30);
if (!locked) {
throw new ASException("操作太频繁,请稍后再试", 429);
}
try {
// 2. 查找/创建设备
DeviceEntity device = findOrCreateDevice(mac, wifiMac, deviceUid, deviceSn);
boolean isNew = device.getId() == null;
Date now = new Date();
if (isNew) {
if (StringUtils.isNotBlank(mac)) device.setMac(mac);
if (StringUtils.isNotBlank(wifiMac)) device.setWifiMac(wifiMac);
if (StringUtils.isNotBlank(deviceUid)) device.setDeviceUid(deviceUid);
if (StringUtils.isNotBlank(deviceSn)) device.setDeviceSn(deviceSn);
device.setLastHeartbeat(now);
device.setCreatedAt(now);
device.setDeleteFlag("UNDEL");
device.setStatus("ACTIVE");
device.setSource("PARTNER");
device.setPartnerId(partnerId);
deviceService.saveDevice(device);
}
// 3. 幂等检查:设备已有有效天卡则直接返回(防重复消耗)
DayCardPoolEntity existing = getDeviceActiveCard(device.getId());
if (existing != null) {
Map<String, Object> idempotent = new HashMap<>();
idempotent.put("deviceId", device.getId());
idempotent.put("thirdCardNo", existing.getThirdCardNo());
idempotent.put("expireAt", existing.getExpireAt());
idempotent.put("status", "ACTIVE");
logger.info("代销天卡消耗幂等返回: deviceId={}, poolId={}", device.getId(), existing.getId());
return idempotent;
}
// 4. 从 partner 池子取一张可用天卡(FOR UPDATE SKIP LOCKED 防并发超卖)
PartnerCardPoolEntity pcp = this.getOne(
new QueryWrapper<PartnerCardPoolEntity>()
.eq("partner_id", partnerId)
.eq("status", "AVAILABLE")
.eq("delete_flag", "UNDEL")
.orderByAsc("id")
.last("LIMIT 1 FOR UPDATE SKIP LOCKED")
);
if (pcp == null) {
throw new ASException("天卡池库存不足", 400);
}
// 5. 绑定天卡到设备:day_card_pool RESERVED -> USED + deviceId + expireAt(默认24h)
Date defaultExpire = new Date(now.getTime() + 24 * 60 * 60 * 1000L);
int updated = dayCardPoolDao.update(null, new UpdateWrapper<DayCardPoolEntity>()
.eq("id", pcp.getPoolId())
.eq("status", DayCardConstants.POOL_STATUS_RESERVED)
.set("status", DayCardConstants.POOL_STATUS_USED)
.set("device_id", device.getId())
.set("activate_status", DayCardConstants.ACTIVATE_STATUS_PENDING)
.set("used_at", now)
.set("expire_at", defaultExpire)
.set("updated_at", now)
);
if (updated == 0) {
throw new ASException("天卡分配失败,请重试", 500);
}
// 6. 更新 partner 池子:CONSUMED + deviceId + channelOrgId
pcp.setStatus("CONSUMED");
pcp.setDeviceId(device.getId());
pcp.setChannelOrgId(channelOrgId);
pcp.setConsumedAt(now);
pcp.setUpdatedAt(now);
this.updateById(pcp);
// 7. 异步激活天卡(调橘视厅)
final Long poolId = pcp.getPoolId();
final Long deviceId = device.getId();
activateExecutor.execute(() -> activateCardAsync(poolId, deviceId));
// 8. 返回天卡信息(异步激活中,客户端通过checkBenefits轮询确认)
// thirdCardNo不暴露给第三方,App通过init+checkBenefits自行获取播放凭证
Map<String, Object> result = new HashMap<>();
result.put("deviceId", device.getId());
result.put("expireAt", defaultExpire);
result.put("status", "ACTIVATING");
logger.info("代销天卡消耗: partnerId={}, deviceId={}, poolId={}, thirdCardNo={}",
partnerId, device.getId(), poolId, pcp.getThirdCardNo());
return result;
} finally {
redisUtils.delete(lockKey);
}
}
// ==================== 回退 ====================
@Override
public int revert(String partnerId, int quantity) {
if (quantity <= 0) {
throw new ASException("回退数量必须大于0", 400);
}
List<PartnerCardPoolEntity> cards = getAvailableCards(partnerId, quantity);
if (cards.isEmpty()) {
return 0;
}
Date now = new Date();
for (PartnerCardPoolEntity card : cards) {
// 平台天卡池 RESERVED -> UNUSED(恢复可分配)
dayCardPoolDao.update(null, new UpdateWrapper<DayCardPoolEntity>()
.eq("id", card.getPoolId())
.eq("status", DayCardConstants.POOL_STATUS_RESERVED)
.set("status", DayCardConstants.POOL_STATUS_UNUSED)
.set("updated_at", now)
);
// partner池子标记DISABLED
card.setStatus("DISABLED");
card.setUpdatedAt(now);
this.updateById(card);
}
logger.info("合作伙伴回退天卡: partnerId={}, quantity={}", partnerId, cards.size());
return cards.size();
}
/** 按设备标识查找已有设备,找不到返回新实体(标识优先级:mac > wifiMac > deviceUid > deviceSn) */
private DeviceEntity findOrCreateDevice(String mac, String wifiMac, String deviceUid, String deviceSn) {
DeviceEntity existing = null;
if (StringUtils.isNotBlank(mac)) {
existing = deviceDao.selectOne(new QueryWrapper<DeviceEntity>().eq("mac", mac).eq("delete_flag", "UNDEL").last("LIMIT 1"));
}
if (existing == null && StringUtils.isNotBlank(wifiMac)) {
existing = deviceDao.selectOne(new QueryWrapper<DeviceEntity>().eq("wifi_mac", wifiMac).eq("delete_flag", "UNDEL").last("LIMIT 1"));
}
if (existing == null && StringUtils.isNotBlank(deviceUid)) {
existing = deviceDao.selectOne(new QueryWrapper<DeviceEntity>().eq("device_uid", deviceUid).eq("delete_flag", "UNDEL").last("LIMIT 1"));
}
if (existing == null && StringUtils.isNotBlank(deviceSn)) {
existing = deviceDao.selectOne(new QueryWrapper<DeviceEntity>().eq("device_sn", deviceSn).eq("delete_flag", "UNDEL").last("LIMIT 1"));
}
return existing != null ? existing : new DeviceEntity();
}
/** 查询设备当前有效的天卡(status=USED 且未过期) */
private DayCardPoolEntity getDeviceActiveCard(Long deviceId) {
return dayCardPoolDao.selectOne(new QueryWrapper<DayCardPoolEntity>()
.eq("device_id", deviceId)
.eq("status", DayCardConstants.POOL_STATUS_USED)
.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
.isNotNull("expire_at")
.gt("expire_at", new Date())
.orderByDesc("used_at")
.last("LIMIT 1"));
}
/** 异步激活天卡:调橘视厅 getToken + activateCard,更新真实过期时间 */
private void activateCardAsync(Long poolId, Long deviceId) {
// 激活锁:防止同一张卡重复激活(参考生产项目 api:thirdparty:activate:lock:{cardId})
String lockKey = DayCardConstants.REDIS_THIRD_PARTY_ACTIVATE_LOCK_PREFIX + poolId;
boolean locked = redisUtils.setIfAbsent(lockKey, "1", 3600);
if (!locked) {
logger.info("天卡激活任务已存在,跳过: poolId={}", poolId);
return;
}
try {
DayCardPoolEntity pool = dayCardPoolDao.selectById(poolId);
if (pool == null) {
logger.warn("异步激活失败:天卡不存在, poolId={}", poolId);
return;
}
DeviceEntity device = deviceDao.selectById(deviceId);
if (device == null) {
updateActivateStatus(poolId, DayCardConstants.ACTIVATE_STATUS_FAILED, "设备不存在: " + deviceId, null);
return;
}
String thirdCardRef = pool.getThirdCardNo();
if (StringUtils.isBlank(thirdCardRef)) {
updateActivateStatus(poolId, DayCardConstants.ACTIVATE_STATUS_FAILED, "第三方卡号为空", null);
return;
}
// 1. 免密登录获取Token(优先从缓存读取,参考生产项目)
String token;
String memberCode;
String tokenCacheKey = DayCardConstants.REDIS_THIRD_PARTY_TOKEN_PREFIX + thirdCardRef;
@SuppressWarnings("unchecked")
Map<String, String> cached = redisUtils.get(tokenCacheKey, Map.class);
if (cached != null && StringUtils.isNotBlank(cached.get("token"))
&& StringUtils.isNotBlank(cached.get("memberCode"))) {
token = cached.get("token");
memberCode = cached.get("memberCode");
} else {
Map<String, String> tokenResult = jushitingApiService.getToken(device, thirdCardRef);
if (tokenResult == null || StringUtils.isBlank(tokenResult.get("token"))) {
updateActivateStatus(poolId, DayCardConstants.ACTIVATE_STATUS_FAILED, "获取Token失败", null);
return;
}
token = tokenResult.get("token");
memberCode = tokenResult.get("memberCode");
// 缓存token和memberCode,5分钟有效
Map<String, String> cacheData = new HashMap<>();
cacheData.put("token", token);
cacheData.put("memberCode", memberCode);
redisUtils.set(tokenCacheKey, cacheData, 5 * 60);
}
// 2. 卡密激活
JstActivateCardResult activateResult = jushitingApiService.activateCard(
deviceId, pool.getCardCode(), memberCode, token);
if (!activateResult.isSuccess()) {
String errMsg = activateResult.getErrorMessage() != null ? activateResult.getErrorMessage() : "卡密激活返回失败";
updateActivateStatus(poolId, DayCardConstants.ACTIVATE_STATUS_FAILED, errMsg, null);
return;
}
// 3. 确定过期时间
Date expireAt = null;
if (StringUtils.isNotBlank(activateResult.getExpireTime())) {
expireAt = parseExpireTime(activateResult.getExpireTime());
}
if (expireAt == null) {
String vipExpire = jushitingApiService.queryVipExpireTime(deviceId, thirdCardRef);
if (StringUtils.isNotBlank(vipExpire)) {
expireAt = parseExpireTime(vipExpire);
}
}
if (expireAt == null) {
expireAt = new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000L);
}
// 4. 更新过期时间 + 激活成功
updateActivateStatus(poolId, DayCardConstants.ACTIVATE_STATUS_SUCCESS, null, expireAt);
logger.info("代销天卡激活完成: poolId={}, deviceId={}, expireAt={}", poolId, deviceId, expireAt);
} catch (Exception e) {
logger.error("代销天卡激活异常: poolId={}", poolId, e);
updateActivateStatus(poolId, DayCardConstants.ACTIVATE_STATUS_FAILED, e.getMessage(), null);
} finally {
try {
redisUtils.delete(lockKey);
} catch (Exception e) {
logger.warn("释放激活锁失败: key={}", lockKey, e);
}
}
}
private void updateActivateStatus(Long poolId, String status, String error, Date expireAt) {
UpdateWrapper<DayCardPoolEntity> uw = new UpdateWrapper<>();
uw.eq("id", poolId)
.set("activate_status", status)
.set("activate_error", error)
.set("updated_at", new Date());
if (expireAt != null) {
uw.set("expire_at", expireAt).set("activated_at", new Date());
}
dayCardPoolDao.update(null, uw);
}
private static Date parseExpireTime(String time) {
if (time == null || time.isEmpty()) {
return null;
}
java.util.TimeZone tz = java.util.TimeZone.getTimeZone("Asia/Shanghai");
try {
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(tz);
return sdf.parse(time);
} catch (Exception e) {
try {
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(tz);
return sdf.parse(time);
} catch (Exception ignored) {
return null;
}
}
}
}