feat(client): 实现账号设备试用期管理功能
- 新增设备试用期过期时间字段及管理接口 - 实现试用期状态检查与过期提醒逻辑 - 支持账号类型区分试用与付费用户 - 添加设备注册时自动设置3天试用期- 实现VIP状态刷新与过期类型判断 -优化账号列表查询支持按客户端用户名过滤 - 更新客户端设备管理支持试用期控制- 完善登录流程支持试用期状态提示 -修复设备离线通知缺少用户名参数问题 - 调整账号默认设置清除逻辑关联客户端用户名
This commit is contained in:
@@ -32,7 +32,7 @@ import com.ruoyi.system.domain.SysCache;
|
||||
public class CacheController
|
||||
{
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private final static List<SysCache> caches = new ArrayList<SysCache>();
|
||||
{
|
||||
@@ -80,15 +80,16 @@ public class CacheController
|
||||
@GetMapping("/getKeys/{cacheName}")
|
||||
public AjaxResult getCacheKeys(@PathVariable String cacheName)
|
||||
{
|
||||
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
|
||||
return AjaxResult.success(new TreeSet<>(cacheKeys));
|
||||
Set<Object> cacheKeys = redisTemplate.keys(cacheName + "*");
|
||||
return AjaxResult.success(cacheKeys != null ? new TreeSet<>(cacheKeys.stream().map(String::valueOf).collect(java.util.stream.Collectors.toSet())) : new TreeSet<>());
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
|
||||
@GetMapping("/getValue/{cacheName}/{cacheKey}")
|
||||
public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey)
|
||||
{
|
||||
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
|
||||
Object cacheValueObj = redisTemplate.opsForValue().get(cacheKey);
|
||||
String cacheValue = cacheValueObj != null ? cacheValueObj.toString() : null;
|
||||
SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
|
||||
return AjaxResult.success(sysCache);
|
||||
}
|
||||
@@ -97,8 +98,10 @@ public class CacheController
|
||||
@DeleteMapping("/clearCacheName/{cacheName}")
|
||||
public AjaxResult clearCacheName(@PathVariable String cacheName)
|
||||
{
|
||||
Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*");
|
||||
redisTemplate.delete(cacheKeys);
|
||||
Set<Object> cacheKeys = redisTemplate.keys(cacheName + "*");
|
||||
if (cacheKeys != null && !cacheKeys.isEmpty()) {
|
||||
redisTemplate.delete(cacheKeys);
|
||||
}
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@@ -114,8 +117,10 @@ public class CacheController
|
||||
@DeleteMapping("/clearCacheAll")
|
||||
public AjaxResult clearCacheAll()
|
||||
{
|
||||
Collection<String> cacheKeys = redisTemplate.keys("*");
|
||||
redisTemplate.delete(cacheKeys);
|
||||
Set<Object> cacheKeys = redisTemplate.keys("*");
|
||||
if (cacheKeys != null && !cacheKeys.isEmpty()) {
|
||||
redisTemplate.delete(cacheKeys);
|
||||
}
|
||||
return AjaxResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,6 @@ public class ClientAccountController extends BaseController {
|
||||
if (userDevice >= deviceLimit) {
|
||||
return AjaxResult.error("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
|
||||
}
|
||||
|
||||
String token = Jwts.builder()
|
||||
.setHeaderParam("kid", jwtRsaKeyService.getKeyId())
|
||||
.setSubject(username)
|
||||
@@ -161,15 +160,24 @@ public class ClientAccountController extends BaseController {
|
||||
.signWith(SignatureAlgorithm.RS256, jwtRsaKeyService.getPrivateKey())
|
||||
.compact();
|
||||
|
||||
// 检查设备试用期(仅对trial账号生效)
|
||||
boolean deviceTrialExpired = false;
|
||||
if ("trial".equals(account.getAccountType())) {
|
||||
ClientDevice device = clientDeviceMapper.selectByDeviceIdAndUsername(clientId, username);
|
||||
deviceTrialExpired = device != null
|
||||
&& device.getTrialExpireTime() != null
|
||||
&& new Date().after(device.getTrialExpireTime());
|
||||
}
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("token", token);
|
||||
data.put("permissions", account.getPermissions());
|
||||
data.put("accountName", account.getAccountName());
|
||||
data.put("expireTime", account.getExpireTime());
|
||||
data.put("accountType", account.getAccountType());
|
||||
data.put("deviceTrialExpired", deviceTrialExpired);
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证token
|
||||
*/
|
||||
@@ -181,17 +189,35 @@ public class ClientAccountController extends BaseController {
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
String username = (String) claims.get("sub");
|
||||
String clientId = (String) claims.get("clientId");
|
||||
|
||||
ClientAccount account = clientAccountService.selectClientAccountByUsername(username);
|
||||
if (account == null || !"0".equals(account.getStatus())) {
|
||||
return AjaxResult.error("token无效");
|
||||
}
|
||||
|
||||
// 付费账号到期自动降级为试用账号
|
||||
if ("paid".equals(account.getAccountType()) && account.getExpireTime() != null && new Date().after(account.getExpireTime())) {
|
||||
account.setAccountType("trial");
|
||||
clientAccountService.updateClientAccount(account);
|
||||
}
|
||||
|
||||
// 检查设备试用期(仅对trial账号生效)
|
||||
boolean deviceTrialExpired = false;
|
||||
if ("trial".equals(account.getAccountType())) {
|
||||
ClientDevice device = clientDeviceMapper.selectByDeviceIdAndUsername(clientId, username);
|
||||
deviceTrialExpired = device != null
|
||||
&& device.getTrialExpireTime() != null
|
||||
&& new Date().after(device.getTrialExpireTime());
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("username", username);
|
||||
result.put("permissions", account.getPermissions());
|
||||
result.put("accountName", account.getAccountName());
|
||||
result.put("expireTime", account.getExpireTime());
|
||||
result.put("accountType", account.getAccountType());
|
||||
result.put("deviceTrialExpired", deviceTrialExpired);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
@@ -217,7 +243,7 @@ public class ClientAccountController extends BaseController {
|
||||
|
||||
/**
|
||||
* 客户端账号注册
|
||||
* 新设备注册送3天VIP,同一设备ID重复注册不赠送
|
||||
* 新账号注册赠送3天VIP试用期
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public AjaxResult register(@RequestBody Map<String, String> registerData) {
|
||||
@@ -232,16 +258,8 @@ public class ClientAccountController extends BaseController {
|
||||
clientAccount.setStatus("0");
|
||||
clientAccount.setPermissions("{\"amazon\":true,\"rakuten\":true,\"zebra\":true}");
|
||||
clientAccount.setPassword(passwordEncoder.encode(password));
|
||||
|
||||
// 新设备赠送3天VIP
|
||||
ClientDevice existingDevice = clientDeviceMapper.selectByDeviceId(deviceId);
|
||||
int vipDays = (existingDevice == null) ? 3 : 0;
|
||||
|
||||
if (vipDays > 0) {
|
||||
clientAccount.setExpireTime(new Date(System.currentTimeMillis() + vipDays * 24L * 60 * 60 * 1000));
|
||||
} else {
|
||||
clientAccount.setExpireTime(new Date());
|
||||
}
|
||||
clientAccount.setAccountType("trial");
|
||||
clientAccount.setExpireTime(new Date(System.currentTimeMillis() + 3 * 24L * 60 * 60 * 1000));
|
||||
|
||||
int result = clientAccountService.insertClientAccount(clientAccount);
|
||||
if (result <= 0) {
|
||||
@@ -263,6 +281,8 @@ public class ClientAccountController extends BaseController {
|
||||
data.put("permissions", clientAccount.getPermissions());
|
||||
data.put("accountName", clientAccount.getAccountName());
|
||||
data.put("expireTime", clientAccount.getExpireTime());
|
||||
data.put("accountType", clientAccount.getAccountType());
|
||||
data.put("deviceTrialExpired", false);
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
|
||||
@@ -300,6 +320,7 @@ public class ClientAccountController extends BaseController {
|
||||
Date newExpireTime = cal.getTime();
|
||||
|
||||
account.setExpireTime(newExpireTime);
|
||||
account.setAccountType("paid");
|
||||
account.setUpdateBy(getUsername());
|
||||
clientAccountService.updateClientAccount(account);
|
||||
|
||||
|
||||
@@ -97,32 +97,33 @@ public class ClientDeviceController {
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public AjaxResult register(@RequestBody ClientDevice device, HttpServletRequest request) {
|
||||
ClientDevice exists = clientDeviceMapper.selectByDeviceId(device.getDeviceId());
|
||||
String ip = IpUtils.getIpAddr(request);
|
||||
String username = device.getUsername();
|
||||
String deviceId = device.getDeviceId();
|
||||
String os = device.getOs();
|
||||
String deviceName = username + "@" + ip + " (" + os + ")";
|
||||
ClientDevice exists = clientDeviceMapper.selectByDeviceIdAndUsername(deviceId, username);
|
||||
if (exists == null) {
|
||||
// 检查设备数量限制
|
||||
try {
|
||||
checkDeviceLimit(device.getUsername(), device.getDeviceId());
|
||||
checkDeviceLimit(username, deviceId);
|
||||
} catch (RuntimeException e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
device.setIp(ip);
|
||||
device.setStatus("online");
|
||||
device.setLastActiveAt(new java.util.Date());
|
||||
device.setTrialExpireTime(new java.util.Date(System.currentTimeMillis() + 3 * 24L * 60 * 60 * 1000));
|
||||
device.setName(deviceName);
|
||||
clientDeviceMapper.insert(device);
|
||||
} else {
|
||||
exists.setUsername(device.getUsername());
|
||||
exists.setName(deviceName);
|
||||
exists.setOs(device.getOs());
|
||||
exists.setOs(os);
|
||||
exists.setStatus("online");
|
||||
exists.setIp(ip);
|
||||
exists.setLocation(device.getLocation());
|
||||
exists.setLastActiveAt(new java.util.Date());
|
||||
clientDeviceMapper.updateByDeviceId(exists);
|
||||
clientDeviceMapper.updateByDeviceIdAndUsername(exists);
|
||||
}
|
||||
return AjaxResult.success();
|
||||
}
|
||||
@@ -137,6 +138,15 @@ public class ClientDeviceController {
|
||||
clientDeviceMapper.updateByDeviceId(device);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备试用期过期时间
|
||||
*/
|
||||
@PostMapping("/updateExpire")
|
||||
public AjaxResult updateExpire(@RequestBody ClientDevice device) {
|
||||
clientDeviceMapper.updateByDeviceIdAndUsername(device);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
/**
|
||||
* 移除设备
|
||||
* 根据 deviceId 删除设备绑定记录。
|
||||
@@ -144,19 +154,20 @@ public class ClientDeviceController {
|
||||
@PostMapping("/remove")
|
||||
public AjaxResult remove(@RequestBody Map<String, String> body) {
|
||||
String deviceId = body.get("deviceId");
|
||||
String username = body.get("username");
|
||||
if (deviceId == null || deviceId.isEmpty()) {
|
||||
return AjaxResult.error("deviceId不能为空");
|
||||
}
|
||||
ClientDevice exists = clientDeviceMapper.selectByDeviceId(deviceId);
|
||||
ClientDevice exists = clientDeviceMapper.selectByDeviceIdAndUsername(deviceId, username);
|
||||
if (exists == null) {
|
||||
return AjaxResult.success();
|
||||
}
|
||||
if (!"removed".equals(exists.getStatus())) {
|
||||
exists.setStatus("removed");
|
||||
exists.setLastActiveAt(new java.util.Date());
|
||||
clientDeviceMapper.updateByDeviceId(exists);
|
||||
sseHubService.sendEvent(exists.getUsername(), deviceId, "DEVICE_REMOVED", "{}");
|
||||
sseHubService.disconnectDevice(exists.getUsername(), deviceId);
|
||||
clientDeviceMapper.updateByDeviceIdAndUsername(exists);
|
||||
sseHubService.sendEvent(username, deviceId, "DEVICE_REMOVED", "{}");
|
||||
sseHubService.disconnectDevice(username, deviceId);
|
||||
}
|
||||
return AjaxResult.success();
|
||||
}
|
||||
@@ -167,11 +178,13 @@ public class ClientDeviceController {
|
||||
@PostMapping("/offline")
|
||||
public AjaxResult offline(@RequestBody Map<String, String> body) {
|
||||
String deviceId = body.get("deviceId");
|
||||
ClientDevice device = clientDeviceMapper.selectByDeviceId(deviceId);
|
||||
String username = body.get("username");
|
||||
ClientDevice device = clientDeviceMapper.selectByDeviceIdAndUsername(deviceId, username);
|
||||
if (device != null) {
|
||||
device.setStatus("offline");
|
||||
device.setLastActiveAt(new java.util.Date());
|
||||
clientDeviceMapper.updateByDeviceId(device);
|
||||
|
||||
clientDeviceMapper.updateByDeviceIdAndUsername(device);
|
||||
}
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@@ -182,38 +195,37 @@ public class ClientDeviceController {
|
||||
*/
|
||||
@PostMapping("/heartbeat")
|
||||
public AjaxResult heartbeat(@RequestBody ClientDevice device, HttpServletRequest request) {
|
||||
ClientDevice exists = clientDeviceMapper.selectByDeviceId(device.getDeviceId());
|
||||
String ip = IpUtils.getIpAddr(request);
|
||||
String username = device.getUsername();
|
||||
String deviceId = device.getDeviceId();
|
||||
String os = device.getOs();
|
||||
String deviceName = username + "@" + ip + " (" + os + ")";
|
||||
|
||||
|
||||
ClientDevice exists = clientDeviceMapper.selectByDeviceIdAndUsername(deviceId, username);
|
||||
if (exists == null) {
|
||||
// 新设备注册
|
||||
device.setIp(ip);
|
||||
device.setStatus("online");
|
||||
device.setLastActiveAt(new java.util.Date());
|
||||
device.setTrialExpireTime(new java.util.Date(System.currentTimeMillis() + 3 * 24L * 60 * 60 * 1000));
|
||||
device.setName(deviceName);
|
||||
clientDeviceMapper.insert(device);
|
||||
} else if ("removed".equals(exists.getStatus())) {
|
||||
// 被移除设备重新激活
|
||||
exists.setUsername(device.getUsername());
|
||||
exists.setName(deviceName);
|
||||
exists.setOs(device.getOs());
|
||||
exists.setOs(os);
|
||||
exists.setStatus("online");
|
||||
exists.setIp(ip);
|
||||
exists.setLocation(device.getLocation());
|
||||
exists.setLastActiveAt(new java.util.Date());
|
||||
clientDeviceMapper.updateByDeviceId(exists);
|
||||
clientDeviceMapper.updateByDeviceIdAndUsername(exists);
|
||||
} else {
|
||||
// 已存在设备更新
|
||||
exists.setUsername(device.getUsername());
|
||||
exists.setStatus("online");
|
||||
exists.setIp(ip);
|
||||
exists.setLastActiveAt(new java.util.Date());
|
||||
exists.setName(deviceName);
|
||||
clientDeviceMapper.updateByDeviceId(exists);
|
||||
clientDeviceMapper.updateByDeviceIdAndUsername(exists);
|
||||
}
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.web.controller.tool;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
@@ -10,9 +11,7 @@ import com.ruoyi.system.domain.BanmaAccount;
|
||||
import com.ruoyi.system.service.IBanmaAccountService;
|
||||
|
||||
/**
|
||||
* 斑马账号管理(数据库版,极简接口):
|
||||
* - 仅负责账号与 Token 的存取
|
||||
* - 不参与登录/刷新与数据采集,客户端自行处理
|
||||
* 斑马账号管理
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/tool/banma")
|
||||
@@ -22,47 +21,36 @@ public class BanmaOrderController extends BaseController {
|
||||
@Autowired
|
||||
private IBanmaAccountService accountService;
|
||||
|
||||
/**
|
||||
* 查询账号列表(
|
||||
*/
|
||||
@GetMapping("/accounts")
|
||||
public R<?> listAccounts() {
|
||||
List<BanmaAccount> list = accountService.listSimple();
|
||||
return R.ok(list);
|
||||
public R<?> listAccounts(String name) {
|
||||
return R.ok(accountService.listSimple(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或编辑账号(含设为默认)
|
||||
*/
|
||||
@PostMapping("/accounts")
|
||||
public R<?> saveAccount(@RequestBody BanmaAccount body) {
|
||||
Long id = accountService.saveOrUpdate(body);
|
||||
boolean ok = false;
|
||||
try { ok = accountService.refreshToken(id); } catch (Exception ignore) {}
|
||||
return ok ? R.ok(Map.of("id", id)) : R.fail("账号或密码错误,无法获取Token");
|
||||
public R<?> saveAccount(@RequestBody BanmaAccount body, String name) {
|
||||
if (body.getId() == null && accountService.validateAndGetToken(body.getUsername(), body.getPassword()) == null) {
|
||||
throw new RuntimeException("账号或密码错误");
|
||||
}
|
||||
Long id = accountService.saveOrUpdate(body, name);
|
||||
accountService.refreshToken(id);
|
||||
return R.ok(Map.of("id", id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除账号
|
||||
*/
|
||||
@DeleteMapping("/accounts/{id}")
|
||||
public R<?> remove(@PathVariable Long id) {
|
||||
accountService.remove(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/** 手动刷新单个账号 Token */
|
||||
@PostMapping("/accounts/{id}/refresh-token")
|
||||
public R<?> refreshOne(@PathVariable Long id) {
|
||||
accountService.refreshToken(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/** 手动刷新全部启用账号 Token */
|
||||
@PostMapping("/refresh-all")
|
||||
public R<?> refreshAll() {
|
||||
accountService.refreshAllTokens();
|
||||
accountService.listSimple().forEach(account -> accountService.refreshToken(account.getId()));
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user