Compare commits

...

35 Commits

Author SHA1 Message Date
777
873460901a 123 2026-01-23 02:13:05 +08:00
777
491ae3ddd6 123 2026-01-23 00:06:56 +08:00
777
550855360c 123 2026-01-22 00:12:46 +08:00
777
e64478b8ab 123 2026-01-22 00:00:06 +08:00
777
7598710f54 123 2026-01-21 13:45:27 +08:00
777
88f9a67d0c 123 2026-01-20 12:41:36 +08:00
777
a60d62f511 123 2026-01-18 15:41:21 +08:00
777
59b9f2ff0f 123 2026-01-18 13:24:21 +08:00
777
be4a4f00d4 123 2026-01-16 09:59:41 +08:00
777
be17f5ca1d 123 2026-01-15 10:36:58 +08:00
777
44d5ca6ee7 123 2026-01-15 10:36:54 +08:00
777
a5c7752f63 123 2026-01-14 19:23:08 +08:00
777
db3412d676 123 2026-01-14 18:11:35 +08:00
777
6f2ed64dbf 123 2026-01-14 18:09:04 +08:00
777
27edbd9cf6 123 2026-01-14 17:45:39 +08:00
777
235059c96a 123 2026-01-14 17:31:25 +08:00
777
7fdf6eefad 123 2026-01-14 01:39:25 +08:00
777
5af759c924 123 2026-01-14 01:17:00 +08:00
777
9a2332a61b 123 2026-01-14 00:46:33 +08:00
777
a2e9e1e476 123 2026-01-13 09:57:50 +08:00
777
355eb8f9f5 123 2026-01-12 23:17:40 +08:00
777
29701e9e15 123 2026-01-12 20:56:48 +08:00
777
f8dfaad63b 123 2026-01-09 14:47:11 +08:00
777
7ed13f3e2c 123 2026-01-09 01:18:16 +08:00
777
432743addf 123 2026-01-08 17:38:41 +08:00
777
20c5908f34 11 2026-01-07 11:30:24 +08:00
777
56129fb865 Merge branch 'new/v2.0.0' into new/v1.1.0 2025-12-29 17:48:33 +08:00
777
d994034e61 登录改造 2025-12-26 12:22:37 +08:00
777
9cd9841f09 登录改造 2025-12-26 12:16:29 +08:00
777
f0d4b595f4 Merge branch 'new/v2.0.0' into new/v1.1.0 2025-12-24 17:49:42 +08:00
777
5900fd8778 登录改造 2025-12-24 17:48:00 +08:00
777
19f7a18faf 登录改造 2025-12-24 10:43:54 +08:00
777
6918fcdc1d nnnn 2025-12-23 18:36:05 +08:00
777
cdad78f180 nnnn 2025-12-23 18:29:19 +08:00
777
5407996240 nnnn 2025-12-23 17:41:22 +08:00
85 changed files with 2019 additions and 377 deletions

View File

@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Edit"
]
}
}

View File

@@ -8,14 +8,12 @@ CREATE TABLE `cai_prize_info`
`guarantee_draws` int NOT NULL DEFAULT 0 COMMENT '保底抽数0表示无保底谢谢惠顾奖无效',
`min_win_draws` int NOT NULL DEFAULT 0 COMMENT '最低中奖抽数0表示无限制谢谢惠顾奖无效',
`stock` int NOT NULL DEFAULT 0 COMMENT '奖品库存谢谢惠顾奖填0不校验',
`prize_type` tinyint not null comment '奖品类型',
`prize_type` tinyint not null comment '奖品类型 1-谢谢惠顾 2-普通奖 3-大奖',
`prize_price` bigint(20) not null default 0 comment '奖品价值估算',
`auto_give` tinyint not null default 0 comment '是否自动兑奖',
`is_thank` tinyint NOT NULL DEFAULT 0 COMMENT '是否为谢谢惠顾奖0-否1-是(全局仅一个)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_is_thank` (`is_thank`) COMMENT '谢谢惠顾奖索引'
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='奖品基础表';
@@ -32,13 +30,59 @@ CREATE TABLE `cai_prize_online`
`guarantee_draws` int NOT NULL DEFAULT 0 COMMENT '保底抽数0表示无保底谢谢惠顾奖无效',
`min_win_draws` int NOT NULL DEFAULT 0 COMMENT '最低中奖抽数0表示无限制谢谢惠顾奖无效',
`stock` int NOT NULL DEFAULT 0 COMMENT '奖品库存谢谢惠顾奖填0不校验',
`prize_type` tinyint not null comment '奖品类型',
`prize_type` tinyint not null comment '奖品类型 1-谢谢惠顾 2-普通奖 3-大奖',
`prize_price` bigint(20) not null default 0 comment '奖品价值估算',
`auto_give` tinyint not null default 0 comment '是否自动兑奖',
`is_thank` tinyint NOT NULL DEFAULT 0 COMMENT '是否为谢谢惠顾奖0-否1-是(全局仅一个)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_is_thank` (`is_thank`) COMMENT '谢谢惠顾奖索引'
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='已发布奖品表';
alter table `cai_point_change_log`
ADD COLUMN remark varchar(200) null comment '备注';
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(2003344862891581442, '抽奖奖品', '1738072642014617602', '1', 'prizeInfo', 'cai/prizeInfo/index', 1, 0, 'C', '0', '0', 'cai:prizeInfo:list', '#', 'admin', sysdate(), '', null, '抽奖奖品菜单');
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(2003344862891581443, '抽奖奖品查询', 2003344862891581442, '1', '#', '', 1, 0, 'F', '0', '0', 'cai:prizeInfo:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(2003344862891581444, '抽奖奖品新增', 2003344862891581442, '2', '#', '', 1, 0, 'F', '0', '0', 'cai:prizeInfo:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(2003344862891581445, '抽奖奖品修改', 2003344862891581442, '3', '#', '', 1, 0, 'F', '0', '0', 'cai:prizeInfo:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(2003344862891581446, '抽奖奖品删除', 2003344862891581442, '4', '#', '', 1, 0, 'F', '0', '0', 'cai:prizeInfo:remove', '#', 'admin', sysdate(), '', null, '');
CREATE TABLE `cai_prize_winning_record`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '奖品ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`use_point` bigint(20) NOT NULL COMMENT '抽奖消耗',
`prize_id` bigint NOT NULL COMMENT '奖品ID',
`prize_name` varchar(100) NOT NULL COMMENT '奖品名称',
`prize_desc` varchar(500) DEFAULT '' COMMENT '奖品描述',
`prize_img` varchar(255) DEFAULT '' COMMENT '奖品图片地址',
`win_probability` decimal(5, 4) NOT NULL COMMENT '中奖率0-1如0.0100表示1%',
`guarantee_draws` int NOT NULL DEFAULT 0 COMMENT '保底抽数0表示无保底谢谢惠顾奖无效',
`min_win_draws` int NOT NULL DEFAULT 0 COMMENT '最低中奖抽数0表示无限制谢谢惠顾奖无效',
`stock` int NOT NULL DEFAULT 0 COMMENT '奖品库存谢谢惠顾奖填0不校验',
`prize_type` tinyint not null comment '奖品类型 1-谢谢惠顾 2-普通奖 3-大奖',
`prize_price` bigint(20) not null default 0 comment '奖品价值估算',
`auto_give` tinyint not null default 0 comment '是否自动兑奖',
`give_status` tinyint not null default 0 comment '奖品兑换情况 0-未兑换 1-已兑换',
`give_remark` varchar(100) not null comment '兑奖备注说明',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='中奖记录';

25
doc/loginAuth.sql Normal file
View File

@@ -0,0 +1,25 @@
CREATE TABLE `cai_user_login`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '子账户ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`usercode` varchar(100) NOT NULL COMMENT '用户',
`mobile` varchar(100) NOT NULL COMMENT '账户明细说明',
`password` varchar(100) NOT NULL COMMENT '账户明细说明',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `user_id` (`user_id`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
ROW_FORMAT = DYNAMIC COMMENT ='123记录';
INSERT INTO `cai_prize_info` (`id`, `prize_name`, `prize_desc`, `prize_img`, `win_probability`, `guarantee_draws`,
`min_win_draws`, `stock`, `prize_type`, `prize_price`, `auto_give`, `create_time`,
`update_time`)
VALUES (1, '谢谢惠顾', '谢谢惠顾', 'test/2026/01/06/0d6bafa0bb1841eabd745f0bf495640c.png', 0.0000, 0, 0, 0, 1, 0, 1,
'2026-01-06 15:33:11', '2026-01-14 16:53:47');

View File

@@ -71,8 +71,6 @@ public class RechargeOrderController extends BaseController {
@SaCheckPermission("cai:rechargeOrder:list")
@GetMapping("/list")
public TableDataInfo<RechargeOrderAdminVo> list(RechargeOrderAdminVo bo, PageQuery pageQuery) {
String string = identifierGenerator.nextId(null).toString();
log.info("ID============{}",string);
Page<RechargeOrderAdminVo> page = rechargeOrderService.pageAdmin(pageQuery,bo);
return TableDataInfo.build(page);
}

View File

@@ -0,0 +1,46 @@
package com.ruoyi.web.controller.cai.admin.op;
import cn.dev33.satoken.annotation.SaCheckRole;
import com.ruoyi.cai.service.LoginAuthService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cai/op/login")
@Slf4j
public class LoginAuthController {
@Autowired
private LoginAuthService loginAuthService;
@GetMapping("/testLogin")
@SaCheckRole("admin")
public R<Void> testLogin(String passwords) {
if(StringUtils.isEmpty(passwords)){
return R.fail("密码不能为空");
}
List<String> passwordList = Arrays.stream(passwords.split(",")).collect(Collectors.toList());
loginAuthService.testLogin(passwordList);
return R.ok();
}
@GetMapping("/testPassword")
@SaCheckRole("admin")
public R<Boolean> testPassword(String mobile,String password) {
boolean b = loginAuthService.checkPassword(mobile, password);
return R.ok(b);
}
}

View File

@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.cai.app;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.PhoneUtil;
import com.ruoyi.cai.auth.*;
import com.ruoyi.cai.constant.RedisHttpConstant;
import com.ruoyi.cai.dto.app.vo.LoginVo;
import com.ruoyi.cai.enums.CodeEnum;
import com.ruoyi.cai.enums.SystemConfigEnum;
@@ -14,14 +15,19 @@ import com.ruoyi.cai.service.IpBlackService;
import com.ruoyi.cai.service.IpRecordService;
import com.ruoyi.cai.service.SmsVerifyService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.cai.util.PasswordUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@@ -65,6 +71,10 @@ public class AuthAppController {
if(!mobile){
return R.fail(600,"请输入正确的手机格式");
}
PasswordUtil.PasswordValidationResult result = PasswordUtil.validatePassword(caiUser.getPassword());
if(!result.isValid()){
return R.fail(600,result.getErrorMessage());
}
String token = caiLoginManager.register(caiUser);
LoginVo vo = new LoginVo();
vo.setToken(token);
@@ -106,7 +116,8 @@ public class AuthAppController {
}
@PostMapping("/register/code")
@Deprecated
// @PostMapping("/register/code")
@Operation(summary = "获取注册验证码")
@Log(title = "获取注册验证码", businessType = BusinessType.OTHER, isSaveDb = false)
public R<Map<String,String>> registerCode(@Validated @RequestBody RegisterCode code){
@@ -143,6 +154,13 @@ public class AuthAppController {
return R.fail(600,"9000009");
}
ipBlackService.checkIpThrowException(ServletUtils.getClientIP());
if(StringUtils.isBlank(code.getUserIp())){
code.setUserIp(ServletUtils.getClientIP());
}
boolean check = verificationCodeCheck.check(code.getTicket(), code.getUserIp(), code.getRandStr());
if(!check){
throw new ServiceException("图形验证码错误");
}
try {
smsVerifyService.put(CodeEnum.RESET_PASSWORD,code.getMobile());
}catch (Exception e){
@@ -159,10 +177,69 @@ public class AuthAppController {
@Autowired
private IpBlackService ipBlackService;
@PostMapping("/login")
@PostMapping("/loginV2")
@Operation(summary = "登陆")
@Log(title = "登陆", businessType = BusinessType.OTHER, isSaveDb = false)
public R<LoginVo> loginV2(@Validated @RequestBody LoginCaiUser loginBody){
LoginVo vo = new LoginVo();
ipBlackService.checkIpThrowException(ServletUtils.getClientIP());
boolean needVerificationCode = this.checkNeedVerificationCode(loginBody);
if(needVerificationCode){
vo.setLoginSuccess(false);
vo.setNeedVerificationCode(true);
return R.ok(vo);
}
try {
String token = caiLoginManager.login(loginBody.getUsername(), loginBody.getPassword());
vo.setLoginSuccess(true);
vo.setToken(token);
vo.setUserInfo(currentUserManager.currentInfo());
}catch (Exception e){
loginAfterManager.loginAfter(loginBody,false,e.getMessage());
ipRecordService.saveLoginIp(ServletUtils.getClientIP());
throw e;
}
loginAfterManager.loginAfter(loginBody,true,"登录成功");
return R.ok(vo);
}
@Autowired
private RedissonClient redissonClient;
private boolean checkNeedVerificationCode(LoginCaiUser loginBody){
if(StringUtils.isNotEmpty(loginBody.getTicket()) || StringUtils.isNotEmpty(loginBody.getRandStr())){
if(StringUtils.isBlank(loginBody.getUserIp())){
loginBody.setUserIp(ServletUtils.getClientIP());
}
boolean check = verificationCodeCheck.check(loginBody.getTicket(), loginBody.getUserIp(), loginBody.getRandStr());
if(!check){
throw new ServiceException("图形验证码错误");
}
}else{
Integer loginErrorOpenSecurity = systemConfigManager.getSystemConfigOfInt(SystemConfigEnum.LOGIN_ERROR_OPEN_SECURITY);
if(loginErrorOpenSecurity <= 0){
return true;
}
String key = String.format(RedisHttpConstant.CHECK_LOGIN_NUM, loginBody.getUsername());
RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
if(atomicLong.get() >= loginErrorOpenSecurity){
return true;
}
}
return false;
}
// @PostMapping("/login")
@Operation(summary = "登陆")
@Log(title = "登陆", businessType = BusinessType.OTHER, isSaveDb = false)
public R<LoginVo> login(@Validated @RequestBody LoginCaiUser loginBody){
boolean openOldLoginApi = systemConfigManager.getSystemConfigOfBool(SystemConfigEnum.OPEN_OLD_LOGIN_API);
if(!openOldLoginApi){
return R.fail("404");
}
LoginVo vo = new LoginVo();
ipBlackService.checkIpThrowException(ServletUtils.getClientIP());
try {
@@ -170,11 +247,11 @@ public class AuthAppController {
vo.setToken(token);
vo.setUserInfo(currentUserManager.currentInfo());
}catch (Exception e){
loginAfterManager.loginAfter(loginBody,false,e.getMessage());
ipRecordService.saveLoginIp(ServletUtils.getClientIP());
throw e;
}
// 异步调用通知
// loginAfterManager.loginAfter(LoginHelper.getUserId());
loginAfterManager.loginAfter(loginBody,true,"登录成功");
return R.ok(vo);
}

View File

@@ -0,0 +1,91 @@
package com.ruoyi.web.controller.cai.app;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.PrizeOnline;
import com.ruoyi.cai.domain.PrizeWinningRecord;
import com.ruoyi.cai.dto.app.draw.resp.*;
import com.ruoyi.cai.lottery.DrawService;
import com.ruoyi.cai.lottery.LotteryService;
import com.ruoyi.cai.service.PrizeOnlineService;
import com.ruoyi.cai.service.PrizeWinningRecordService;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.BeanConvertUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/draw")
@Tag(name = "抽奖相关接口")
public class DrawController {
@Autowired
private DrawService drawService;
@Autowired
private PrizeOnlineService prizeOnlineService;
@Autowired
private PrizeWinningRecordService prizeWinningRecordService;
@Autowired
private LotteryService lotteryService;
@GetMapping("/baseConfig")
@Operation(summary = "获取抽奖相关的基础信息")
public R<DrawBaseConfigResp> baseConfig(){
DrawBaseConfigResp resp = drawService.baseConfig();
return R.ok(resp);
}
@GetMapping("/points/logs")
@Operation(summary = "获取积分记录")
public R<List<PointLogsResp>> pointsLogs(PageQuery page){
List<PointLogsResp> resp = drawService.pointsLogs(page);
return R.ok(resp);
}
@GetMapping("/prize/up")
@Operation(summary = "获取奖品列表")
public R<List<PrizeOnlineResp>> prizeUp(){
List<PrizeOnline> prizeOnlines = prizeOnlineService.prizeUp();
List<PrizeOnlineResp> resp = BeanConvertUtil.convertListTo(prizeOnlines, PrizeOnlineResp::new);
return R.ok(resp);
}
@GetMapping("/winning/record")
@Operation(summary = "获取自己的中奖记录")
public R<List<UserWinningRecordResp>> winningRecord(PageQuery page){
Page<PrizeWinningRecord> page1 = prizeWinningRecordService.page(page.build(), Wrappers.lambdaQuery(PrizeWinningRecord.class)
.eq(PrizeWinningRecord::getUserId, LoginHelper.getUserId())
.orderByDesc(PrizeWinningRecord::getCreateTime)
);
List<UserWinningRecordResp> resps = BeanConvertUtil.convertListTo(page1.getRecords(), UserWinningRecordResp::new);
return R.ok(resps);
}
@GetMapping("/winning/center")
@Operation(summary = "全局中奖记录")
public R<List<CenterWinningRecordResp>> winningCenter(){
List<PrizeWinningRecord> list = prizeWinningRecordService.list(Wrappers.lambdaQuery(PrizeWinningRecord.class)
.orderByDesc(PrizeWinningRecord::getCreateTime));
List<CenterWinningRecordResp> resps = BeanConvertUtil.convertListTo(list, CenterWinningRecordResp::new);
resps.forEach(i -> i.setUserName("****"));
return R.ok(resps);
}
@GetMapping("/lottery")
@Operation(summary = "抽奖接口")
public R<PrizeOnlineResp> lottery(){
PrizeOnline draw = lotteryService.draw(LoginHelper.getUserId());
PrizeOnlineResp resp = BeanConvertUtil.convertTo(draw, PrizeOnlineResp::new);
return R.ok(resp);
}
}

View File

@@ -84,7 +84,7 @@ public class FileController {
if (ObjectUtil.isNull(file)) {
return R.fail("上传文件不能为空");
}
SysOssVo oss = iSysOssService.upload(file);
SysOssVo oss = iSysOssService.upload(file,"tokenKey");
FileResp resp = new FileResp();
resp.setUrl(oss.getUrl());
resp.setPath(oss.getFileName());

View File

@@ -1,5 +1,6 @@
package com.ruoyi.web.controller.cai.app;
import cn.hutool.core.util.RandomUtil;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.dto.app.RankIdReq;
import com.ruoyi.cai.dto.app.vo.anchor.AnchorStatusDTO;
@@ -272,10 +273,12 @@ public class RankAppController {
if(StringUtils.isEmpty(nickname)){
return "*";
}
if(containsEmoji(nickname)){
return "*";
}
return "*"+nickname.substring(nickname.length()-1);
char c = RandomUtil.randomChinese();
return "*"+c;
// if(containsEmoji(nickname)){
// return "*密";
// }
// return "*"+nickname.substring(nickname.length()-1);
}
public static void main(String[] args) throws Exception {

View File

@@ -22,7 +22,7 @@ spring:
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://124.222.254.188:4306/cai_v6?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: root
password: tyYrk487R4y7FENM
password: Zl930329!
# 从库数据源
slave:
lazy: true
@@ -57,7 +57,7 @@ spring:
# 数据库索引
database: 12
# 密码(如没有密码请注释掉)
password: dsjakldbwja
password: WggDVPbn4eLpoX7
# 连接超时时间
timeout: 15s
# 是否开启ssl
@@ -65,7 +65,7 @@ spring:
rabbitmq:
addresses: 124.222.254.188 #ip地址
username: admin # 账号
password: THnpGkdS # 密码
password: WggDVPbn4eLpoX7 # 密码
port: 5672
virtual-host: /cai-dev
@@ -120,3 +120,9 @@ cai:
proxy-host: 7693
home-name: 知予
coin-name: 知钻
tencent:
captcha:
app-secret-key: wCmccPiqdW1C8V3t7GdAIYB3Z
captcha-app-id: 189992647
secret-id: IKID8EXQFxZ2NbjiZHqGZjjaHpEh7OgruoZB
secret-key: a3NbjkgTBbzNrFcwRtDORpYezddhgeWc

View File

@@ -13,6 +13,25 @@
</encoder>
</appender>
<!-- 控制台输出 -->
<appender name="file_login" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/login/sys-login.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/login/sys-login.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-console.log</file>
@@ -117,6 +136,10 @@
<!-- </encoder>-->
<!-- </appender>-->
<logger name="com.ruoyi.cai.manager.LoginAfterManager" level="INFO" additivity="false">
<appender-ref ref="file_login" /> <!-- 绑定到file_login Appender -->
</logger>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console" />

View File

@@ -14,4 +14,8 @@ public class LoginCaiUser {
@Schema(description = "密码")
@NotEmpty(message = "密码不能为空")
private String password;
private String ticket;
private String randStr;
private String userIp;
}

View File

@@ -8,4 +8,10 @@ import javax.validation.constraints.NotEmpty;
public class RegisterCode {
@NotEmpty(message = "手机号不能为空")
private String mobile;
@NotEmpty(message = "腾讯验证码参数[ticket]")
private String ticket;
@NotEmpty(message = "腾讯验证码所属参数[randStr]")
private String randStr;
private String userIp;
}

View File

@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 抽奖奖品
@@ -46,6 +47,11 @@ public class PrizeInfoController extends BaseController {
return TableDataInfo.build(page);
}
@GetMapping("/all")
public R<List<PrizeInfo>> all() {
return R.ok(prizeInfoService.list());
}
/**
* 获取抽奖奖品详细信息
*
@@ -77,6 +83,9 @@ public class PrizeInfoController extends BaseController {
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody PrizeInfo bo) {
if(bo.getId() == 1){
return R.fail("无法编辑系统内置谢谢惠顾");
}
return toAjax(prizeInfoService.updateById(bo));
}
@@ -90,6 +99,10 @@ public class PrizeInfoController extends BaseController {
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(prizeInfoService.removeBatchByIds(Arrays.asList(ids), true));
List<Long> idArray = Arrays.asList(ids);
if(idArray.contains(1L)){
return R.fail("无法删除系统内置谢谢惠顾");
}
return toAjax(prizeInfoService.removeBatchByIds(idArray, true));
}
}

View File

@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 已发布奖品
@@ -41,9 +42,9 @@ public class PrizeOnlineController extends BaseController {
*/
@SaCheckPermission("cai:prizeOnline:list")
@GetMapping("/list")
public TableDataInfo<PrizeOnline> list(PrizeOnline bo, PageQuery pageQuery) {
Page<PrizeOnline> page = prizeOnlineService.page(pageQuery.build(), Wrappers.lambdaQuery(PrizeOnline.class));
return TableDataInfo.build(page);
public R<List<PrizeOnline>> list(PrizeOnline bo) {
List<PrizeOnline> page = prizeOnlineService.list(Wrappers.lambdaQuery(bo));
return R.ok(page);
}
/**
@@ -64,32 +65,10 @@ public class PrizeOnlineController extends BaseController {
@SaCheckPermission("cai:prizeOnline:add")
@Log(title = "已发布奖品", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody PrizeOnline bo) {
return toAjax(prizeOnlineService.save(bo));
@PostMapping("/reset/{gender}")
public R<Void> reset(@PathVariable Integer gender,@Validated(AddGroup.class) @RequestBody List<PrizeOnline> bo) {
prizeOnlineService.reset(gender,bo);
return R.ok();
}
/**
* 修改已发布奖品
*/
@SaCheckPermission("cai:prizeOnline:edit")
@Log(title = "已发布奖品", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody PrizeOnline bo) {
return toAjax(prizeOnlineService.updateById(bo));
}
/**
* 删除已发布奖品
*
* @param ids 主键串
*/
@SaCheckPermission("cai:prizeOnline:remove")
@Log(title = "已发布奖品", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(prizeOnlineService.removeBatchByIds(Arrays.asList(ids), true));
}
}

View File

@@ -0,0 +1,94 @@
package com.ruoyi.cai.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.PrizeWinningRecord;
import com.ruoyi.cai.dto.admin.vo.winningRecord.PrizeWinningRecordAdminVO;
import com.ruoyi.cai.mapper.PrizeWinningRecordMapper;
import com.ruoyi.cai.service.PrizeWinningRecordService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
/**
* 中奖记录
*
* @author ruoyi
* @date 2026-01-08
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cai/prizeWinningRecord")
public class PrizeWinningRecordController extends BaseController {
private final PrizeWinningRecordService prizeWinningRecordService;
@Resource
private PrizeWinningRecordMapper prizeWinningRecordMapper;
/**
* 查询中奖记录列表
*/
@SaCheckPermission("cai:prizeWinningRecord:list")
@GetMapping("/list")
public TableDataInfo<PrizeWinningRecordAdminVO> list(PrizeWinningRecordAdminVO bo, PageQuery pageQuery) {
Page<PrizeWinningRecordAdminVO> page = prizeWinningRecordMapper.pageAdmin(pageQuery.build(), bo);
return TableDataInfo.build(page);
}
/**
* 获取中奖记录详细信息
*
* @param id 主键
*/
@SaCheckPermission("cai:prizeWinningRecord:query")
@GetMapping("/{id}")
public R<PrizeWinningRecord> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(prizeWinningRecordService.getById(id));
}
/**
* 修改中奖记录
*/
@SaCheckPermission("cai:prizeWinningRecord:edit")
@Log(title = "中奖记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/give")
public R<Void> give(@Validated(EditGroup.class) @RequestBody PrizeWinningRecord bo) {
PrizeWinningRecord update = new PrizeWinningRecord();
update.setId(bo.getId());
update.setGiveStatus(1);
update.setGiveRemark(bo.getGiveRemark());
prizeWinningRecordService.updateById(update);
return R.ok();
}
/**
* 删除中奖记录
*
* @param ids 主键串
*/
@SaCheckPermission("cai:prizeWinningRecord:remove")
@Log(title = "中奖记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(prizeWinningRecordService.removeBatchByIds(Arrays.asList(ids), true));
}
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.cai.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.joda.time.LocalDateTime;
import java.io.Serializable;
@Data
@TableName("cai_user_login")
public class LoginAuth implements Serializable {
@TableId(value = "id",type = IdType.AUTO)
private Long id;
private Long userId;
private String usercode;
private String mobile;
private String password;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}

View File

@@ -86,6 +86,7 @@ public class PointChangeLog implements Serializable {
* 是否兑换
*/
private Integer giveFlag;
private String remark;
/**
* 兑换时间
*/

View File

@@ -45,19 +45,19 @@ public class PrizeInfo implements Serializable {
*/
private BigDecimal winProbability;
/**
* 保底抽数0表示无保底,谢谢惠顾奖无效
* 保底抽数0表示无保底
*/
private Long guaranteeDraws;
/**
* 最低中奖抽数0表示无限制,谢谢惠顾奖无效
* 最低中奖抽数0表示无限制
*/
private Long minWinDraws;
/**
* 奖品库存谢谢惠顾奖填0,不校验
* 奖品库存谢谢惠顾奖填0
*/
private Long stock;
/**
* 奖品类型
* 奖品类型 1-谢谢惠顾 2-普通奖 3-大奖
*/
private Long prizeType;
/**
@@ -68,10 +68,6 @@ public class PrizeInfo implements Serializable {
* 是否自动兑奖
*/
private Boolean autoGive;
/**
* 是否为谢谢惠顾奖0-否1-是(全局仅一个)
*/
private Boolean thank;
private LocalDateTime createTime;
private LocalDateTime updateTime;

View File

@@ -55,19 +55,19 @@ public class PrizeOnline implements Serializable {
/**
* 保底抽数0表示无保底谢谢惠顾奖无效
*/
private Long guaranteeDraws;
private Integer guaranteeDraws;
/**
* 最低中奖抽数0表示无限制谢谢惠顾奖无效
*/
private Long minWinDraws;
private Integer minWinDraws;
/**
* 奖品库存谢谢惠顾奖填0不校验
*/
private Long stock;
/**
* 奖品类型
* 奖品类型 1-谢谢惠顾 2-普通奖 3-大奖
*/
private Long prizeType;
private Integer prizeType;
/**
* 奖品价值估算
*/
@@ -76,10 +76,6 @@ public class PrizeOnline implements Serializable {
* 是否自动兑奖
*/
private Boolean autoGive;
/**
* 是否为谢谢惠顾奖0-否1-是(全局仅一个)
*/
private Boolean thank;
private LocalDateTime createTime;
private LocalDateTime updateTime;

View File

@@ -0,0 +1,93 @@
package com.ruoyi.cai.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 中奖记录对象 cai_prize_winning_record
*
* @author ruoyi
* @date 2026-01-08
*/
@Data
@TableName("cai_prize_winning_record")
public class PrizeWinningRecord implements Serializable {
private static final long serialVersionUID=1L;
/**
* 奖品ID
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 抽奖消耗
*/
private Integer usePoint;
/**
* 奖品ID
*/
private Long prizeId;
/**
* 奖品名称
*/
private String prizeName;
/**
* 奖品描述
*/
private String prizeDesc;
/**
* 奖品图片地址
*/
private String prizeImg;
/**
* 中奖率0-1如0.0100表示1%
*/
private BigDecimal winProbability;
/**
* 保底抽数0表示无保底谢谢惠顾奖无效
*/
private Integer guaranteeDraws;
/**
* 最低中奖抽数0表示无限制谢谢惠顾奖无效
*/
private Integer minWinDraws;
/**
* 奖品库存谢谢惠顾奖填0不校验
*/
private Long stock;
/**
* 奖品类型 1-谢谢惠顾 2-普通奖 3-大奖
*/
private Integer prizeType;
/**
* 奖品价值估算
*/
private Long prizePrice;
/**
* 是否自动兑奖
*/
private Boolean autoGive;
/**
* 奖品兑换情况 0-未兑换 1-已兑换
*/
private Integer giveStatus;
/**
* 兑奖备注说明
*/
private String giveRemark;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}

View File

@@ -43,6 +43,7 @@ public class UserInfo {
private BigDecimal payIncomeRate;
/**
*/
@Deprecated
private BigDecimal pointRate;
/**
* 登录次数

View File

@@ -0,0 +1,14 @@
package com.ruoyi.cai.dto.admin.vo.winningRecord;
import com.ruoyi.cai.domain.PrizeWinningRecord;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.enums.SensitiveStrategy;
import lombok.Data;
@Data
public class PrizeWinningRecordAdminVO extends PrizeWinningRecord {
private String usercode;
private String nickname;
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String mobile;
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.cai.dto.app.draw.resp;
import lombok.Data;
import org.joda.time.LocalDateTime;
@Data
public class CenterWinningRecordResp {
/**
* 用户昵称
*/
private String userName;
/**
* 抽奖消耗
*/
private Long usePoint;
/**
* 奖品名称
*/
private String prizeName;
private String prizeDesc;
/**
* 奖品图片地址
*/
private String prizeImg;
/**
* 奖品兑换情况 0-未兑换 1-已兑换
*/
private Integer giveStatus;
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,10 @@
package com.ruoyi.cai.dto.app.draw.resp;
import lombok.Data;
@Data
public class DrawBaseConfigResp {
private boolean openDraw;
private Integer drawPoint;
private String drawText;
}

View File

@@ -0,0 +1,14 @@
package com.ruoyi.cai.dto.app.draw.resp;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class PointLogsResp {
// 1-充值 2-系统调整 3-分销 4-抽奖
private String actionType;
private String message;
private String tarImg;
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,28 @@
package com.ruoyi.cai.dto.app.draw.resp;
import lombok.Data;
@Data
public class PrizeOnlineResp {
private Long id;
/**
* 奖品名称
*/
private String prizeName;
/**
* 奖品描述
*/
private String prizeDesc;
/**
* 奖品图片地址
*/
private String prizeImg;
/**
* 奖品类型 1-谢谢惠顾 2-普通奖 3-大奖
*/
private Integer prizeType;
/**
* 奖品价值估算
*/
private Long prizePrice;
}

View File

@@ -0,0 +1,36 @@
package com.ruoyi.cai.dto.app.draw.resp;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserWinningRecordResp {
/**
* 中奖记录ID
*/
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 抽奖消耗
*/
private Integer usePoint;
/**
* 奖品名称
*/
private String prizeName;
private String prizeDesc;
/**
* 奖品图片地址
*/
private String prizeImg;
/**
* 奖品兑换情况 0-未兑换 1-已兑换
*/
private Integer giveStatus;
private LocalDateTime createTime;
}

View File

@@ -4,6 +4,10 @@ import lombok.Data;
@Data
public class LoginVo {
// 是否登录成功
private boolean loginSuccess;
// 是否需要图形验证码
private boolean needVerificationCode;
private String token;
private CurrentUserInfoVo userInfo;
}

View File

@@ -22,4 +22,6 @@ public class UserAccountVo {
@Schema(description = "收益的紫贝")
private Long incomeCoin;
private Long points;
}

View File

@@ -19,6 +19,16 @@ public enum GenderEnum {
this.defaultAvatar = defaultAvatar;
}
public static boolean isSelect(Integer code){
if(GenderEnum.WOMEN.getCode().equals(code)){
return true;
}
if(GenderEnum.MAN.getCode().equals(code)){
return true;
}
return false;
}
public static GenderEnum getByCode(Integer code){
GenderEnum[] values = GenderEnum.values();
for (GenderEnum value : values) {

View File

@@ -15,10 +15,13 @@ public enum SystemConfigEnum {
/**
* 安全配置
*/
OPEN_IP_NUMBER("5", "IP每日登录次数超过多少次封",SystemConfigGroupEnum.SECURITY),
OPEN_IP_NUMBER("10", "IP每日登录次数超过多少次封",SystemConfigGroupEnum.SECURITY),
LOGIN_ERROR_OPEN_SECURITY("2", "输入密码次数超过多少次开启图形验证码",SystemConfigGroupEnum.SECURITY),
OPEN_OLD_LOGIN_API("0", "开启旧版无验证码登录接口",SystemConfigGroupEnum.SECURITY),
OPEN_OLD_REGISTER_CODE("0", "是否开启无验证码注册接口",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck()),
OPEN_IP_AUTO("1", "开启自动定时封IP",SystemConfigGroupEnum.SECURITY),
OPEN_RESET_PASSWORD("1", "开启重置密码",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck()),
LOGIN_PASSWORD_ERROR_MAX_NUM("5", "登录输错密码上限",SystemConfigGroupEnum.SECURITY, new NumberSystemConfigCheck()),
LOGIN_PASSWORD_ERROR_MAX_NUM("20", "登录输错密码上限",SystemConfigGroupEnum.SECURITY, new NumberSystemConfigCheck()),
SENSITIVE_ENABLE("1", "是否开启手机号脱敏",SystemConfigGroupEnum.SECURITY,new BooleanSystemConfigCheck()),
OPEN_CLEAN_DYNAMIC("1", "开启动态定时清除",SystemConfigGroupEnum.SECURITY,new BooleanSystemConfigCheck()),
YUNXIN_ONLINE_ENABLE("1", "是否开启云信监控在线状态",SystemConfigGroupEnum.SECURITY,new BooleanSystemConfigCheck()),
@@ -29,10 +32,9 @@ public enum SystemConfigEnum {
IPV6_FILTER("0", "是否开启IPV6请求拦截",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck()),
IPV6_FILTER_PATH("/api/auth/login", "IPV6拦截路由配置逗号分隔",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck(),"textarea"),
VIP_PRIVATE_PLUS("0", "开启VIP隐私模式增强模式",SystemConfigGroupEnum.SECURITY,new BooleanSystemConfigCheck()),
OPEN_ANCHOR_CHAT_COUNT("0", "开启主播主动消息统计",SystemConfigGroupEnum.SECURITY,new BooleanSystemConfigCheck()),
OPEN_ANCHOR_CHAT_COUNT("1", "开启主播主动消息统计",SystemConfigGroupEnum.SECURITY,new BooleanSystemConfigCheck()),
// 4-recordId拦截 5-recordId加强拦截 6-性别拦截 7-vip加强拦截
IM_FILTER_PLUS("0", "IM拦截配置勿动开发配置",SystemConfigGroupEnum.SECURITY),
OPEN_OLD_REGISTER_CODE("0", "是否开启无验证码注册接口",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck()),
IM_FILTER_PLUS("4", "IM拦截配置勿动开发配置",SystemConfigGroupEnum.SECURITY),
OPEN_NOTICE("1", "是否开启告警",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck()),
PAY_ERROR_NUM_NOTICE("2", "连续调用支付失败N次发起告警",SystemConfigGroupEnum.SECURITY, new NumberSystemConfigCheck()),
PAY_NOTIFY_ERROR_NUM_NOTICE("5", "连续调用N次支付但是依旧未支付成功发起告警",SystemConfigGroupEnum.SECURITY, new NumberSystemConfigCheck()),
@@ -59,6 +61,33 @@ public enum SystemConfigEnum {
V12_XIAOCHENGXU_ORG_ID("gh_62790d4f9c57", "V12德商小程序原始id",SystemConfigGroupEnum.PAY),
V12_XIAOCHENGXU_PATH("pages/zf/index?", "V12德商小程序页面路径",SystemConfigGroupEnum.PAY),
V12_WX_APP_ID("wxae39c7eed3221d26", "微信开放平台ID",SystemConfigGroupEnum.PAY),
/**
* 抽奖和积分
*/
DEFAULT_PAY_POINT_RATE("0", "分销上级充值的积分提成",SystemConfigGroupEnum.DRAW,new RateAllowZeroSystemConfigCheck()),
OPEN_DRAW_WOMEN("1","是否开启女用户积分抽奖",SystemConfigGroupEnum.DRAW,new BooleanSystemConfigCheck()),
OPEN_DRAW_MAN("1","是否开启男用户积分抽奖",SystemConfigGroupEnum.DRAW,new BooleanSystemConfigCheck()),
WOMEN_DRAW_POINT("100","女用户抽奖分数",SystemConfigGroupEnum.DRAW,new NumberSystemConfigCheck()),
MEN_DRAW_POINT("100","男用户抽奖分数",SystemConfigGroupEnum.DRAW,new NumberSystemConfigCheck()),
MAX_POINT_DAY("0","用户每日可获取最大积分0表示不限制",SystemConfigGroupEnum.DRAW,new NumberSystemConfigCheck()),
DRAW_DIALOG_TEXT("当日邀请5个新用户充值奖励108\n" +
"当日邀请10个新用户充值奖励168\n" +
"当日邀请20个新用户充值奖励288\n" +
"当日邀请30个新用户充值奖励388\n" +
"当日邀请50个新用户充值奖励588\n" +
"注:必须是当日邀请 当日充值。发现刷单的一律封号 不给予提现\n" +
"《凌晨12点之后联系平台客服领取》\n" +
"当日邀请榜单活动(发现刷单的一律封号不给予提现)\n" +
"第一名1888\n" +
"第二名999\n" +
"第三名555\n" +
"第四名308\n" +
"第五名188\n" +
"第六名至十名108\n" +
"开服期间邀请充值奖励百分之40\n" +
"魅力榜,邀请榜、日榜、周榜双榜奖励!!!\n" +
"单笔充值120送普通会员\n" +
"单笔充值300送超级会员", "抽奖说明",SystemConfigGroupEnum.DRAW,null,"textarea"),
/**
* 域名配置
*/
@@ -68,6 +97,7 @@ public enum SystemConfigEnum {
ANCHOR_JOIN_AGREEMENT("/#/agreement/anchor-join", "主播入驻协议地址",SystemConfigGroupEnum.DOMAIN),
WS_SOCKET_URL("ws://localhost:8080/ws?token=%s&room_id=%s", "ws通讯地址",SystemConfigGroupEnum.DOMAIN),
UPLOAD_FILE_DOMAIN("http://127.0.0.1:8080", "文件上传域名服务器",SystemConfigGroupEnum.DOMAIN),
PAY_NOTIFY_URL("http://127.0.0.1:8080", "域名回调",SystemConfigGroupEnum.DOMAIN),
// 七牛云 ?imageView2/2/w/120/h/120
// 腾讯云 ?thumbnail=120y120&imageView
IM_ICON_SUFFIX("?thumbnail=120y120&imageView", "im头像后缀",SystemConfigGroupEnum.DOMAIN),
@@ -114,7 +144,6 @@ public enum SystemConfigEnum {
DEFAULT_GIFT_INCOME_RATE("0.07", "默认分销上级礼物提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()),
DEFAULT_GUARD_INCOME_RATE("0.07", "默认分销上级守护提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()),
DEFAULT_PAY_INCOME_RATE("0.3", "默认分销上级充值提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()),
DEFAULT_PAY_POINT_RATE("0.07", "默认分销上级充值的积分提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()),
PAY_INCOME_RATE("0", "分销上级充值提成配置大于0数据后将强制使用该提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()),
DEFAULT_UNION_VIDEO_INCOME_RATE("0.01", "默认工会视频提成",SystemConfigGroupEnum.BUSINESS, new RateSystemConfigCheck()),
DEFAULT_UNION_ONE_INCOME_RATE("0.07", "默认工会一级提成",SystemConfigGroupEnum.BUSINESS, new RateSystemConfigCheck()),

View File

@@ -5,6 +5,7 @@ public enum SystemConfigGroupEnum {
BUSINESS,
SECURITY,
PAY,
DOMAIN
DOMAIN,
DRAW
;
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.cai.enums.prize;
import lombok.Getter;
// 奖品类型 1-谢谢惠顾 2-普通奖 3-大奖
@Getter
public enum PrizeTypeEnum {
NONE(1,"谢谢惠顾"),
NORMAL(2,"普通奖"),
GOOD(3,"大奖"),
;
private final Integer code;
private final String text;
PrizeTypeEnum(Integer code, String text) {
this.code = code;
this.text = text;
}
}

View File

@@ -0,0 +1,36 @@
package com.ruoyi.cai.enums.systemconfig;
import cn.hutool.core.util.NumberUtil;
import com.ruoyi.common.utils.StringUtils;
import javax.net.ssl.SSLSocket;
import java.math.BigDecimal;
public class RateAllowZeroSystemConfigCheck implements ISystemConfigCheck{
@Override
public SystemCheckResp check(String value) {
if(StringUtils.isEmpty(value)){
return SystemCheckResp.fail("该配置必填");
}
boolean b = NumberUtil.isDouble(value);
if(!b){
return SystemCheckResp.fail("请填写(0-1)之间的数字,两位小数点");
}
BigDecimal bigDecimal = new BigDecimal(value);
boolean in = NumberUtil.isIn(bigDecimal, BigDecimal.ZERO, BigDecimal.ONE);
if(!in){
return SystemCheckResp.fail("请填写(0-1)之间的数字,两位小数点");
}
if(bigDecimal.scale() > 2){
return SystemCheckResp.fail("小数点位数只能配置两位");
}
return SystemCheckResp.ok();
}
public static void main(String[] args) {
RateAllowZeroSystemConfigCheck check = new RateAllowZeroSystemConfigCheck();
System.out.println(check.check("0.0").getMessage());
System.out.println(check.check("1").getMessage());
}
}

View File

@@ -133,13 +133,5 @@ public class AliSmsKit {
return sendMessage(phone,messageTemplate,null,true);
}
public static void main(String[] args) {
AliSmsKit messageSenderUtil = new AliSmsKit();
AliSmsProperties config = new AliSmsProperties();
messageSenderUtil.setConfig(config);
messageSenderUtil.init();
messageSenderUtil.sendMessage("15302786929", CodeEnum.REGISTER.getAliTemplate(),"772290");
}
}

View File

@@ -25,11 +25,6 @@ import java.util.Objects;
@Component
public class DuanXinBaoSmsKit {
public static void main(String[] args) {
boolean s = sendMessage("15302786929", RandomUtil.randomNumbers(4));
log.info(s+"");
}
@Autowired
@Setter
private DuanXinBaoSmsProperties config;

View File

@@ -0,0 +1,72 @@
package com.ruoyi.cai.lottery;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.PointChangeLog;
import com.ruoyi.cai.dto.app.draw.resp.DrawBaseConfigResp;
import com.ruoyi.cai.dto.app.draw.resp.PointLogsResp;
import com.ruoyi.cai.dto.commom.user.MinUser;
import com.ruoyi.cai.enums.GenderEnum;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.service.PointChangeLogService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.BeanConvertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DrawService {
@Autowired
private SystemConfigManager systemConfigManager;
@Autowired
private UserService userService;
@Autowired
private PointChangeLogService pointChangeLogService;
public DrawBaseConfigResp baseConfig() {
Long userId = LoginHelper.getUserId();
MinUser minUser = userService.getMinUserById(userId);
DrawBaseConfigResp resp = new DrawBaseConfigResp();
boolean openDraw = getOpenDraw(minUser.getGender());
resp.setOpenDraw(openDraw);
Integer drawPoint = this.getDrawPoint(minUser.getGender());
resp.setDrawPoint(drawPoint);
String drawText = systemConfigManager.getSystemConfig(SystemConfigEnum.DRAW_DIALOG_TEXT);
resp.setDrawText(drawText);
return resp;
}
public Integer getDrawPoint(Integer gender){
if(GenderEnum.WOMEN.getCode().equals(gender)){
Integer womenDrawPoint = systemConfigManager.getSystemConfigOfInt(SystemConfigEnum.WOMEN_DRAW_POINT);
return womenDrawPoint;
}
return systemConfigManager.getSystemConfigOfInt(SystemConfigEnum.MEN_DRAW_POINT);
}
public boolean getOpenDraw(Integer gender){
if(GenderEnum.WOMEN.getCode().equals(gender)){
return systemConfigManager.getSystemConfigOfBool(SystemConfigEnum.OPEN_DRAW_WOMEN);
}else if(GenderEnum.MAN.getCode().equals(gender)){
return systemConfigManager.getSystemConfigOfBool(SystemConfigEnum.OPEN_DRAW_MAN);
}
return false;
}
public List<PointLogsResp> pointsLogs(PageQuery pageQuery) {
Page<PointChangeLog> page = pointChangeLogService.page(pageQuery.build(), Wrappers.lambdaQuery(PointChangeLog.class)
.eq(PointChangeLog::getUserId, LoginHelper.getUserId())
.orderByDesc(PointChangeLog::getCreateTime));
List<PointChangeLog> records = page.getRecords();
List<PointLogsResp> pointLogsResps = BeanConvertUtil.convertListTo(records, PointLogsResp::new);
return pointLogsResps;
}
}

View File

@@ -1,167 +1,338 @@
package com.ruoyi.cai.lottery;
import com.lottery.entity.Prize;
import com.lottery.entity.UserDrawRecord;
import com.lottery.mapper.UserDrawRecordMapper;
import com.ruoyi.cai.domain.PrizeInfo;
import com.ruoyi.cai.domain.Account;
import com.ruoyi.cai.domain.PointChangeLog;
import com.ruoyi.cai.domain.PrizeOnline;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.enums.GenderEnum;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.enums.prize.PrizeTypeEnum;
import com.ruoyi.cai.manager.IdManager;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.service.AccountService;
import com.ruoyi.cai.service.PrizeOnlineService;
import com.ruoyi.cai.service.PrizeWinningRecordService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* 抽奖核心服务(优化版)
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class LotteryService {
private static final Long THANKS_PRIZE_ID = 1L;
private static final String USER_DRAW_COUNT_KEY = "user:draw:count:%s";
// 每个用户的锁防止并发抽奖单机版用ReentrantLock
private final ReentrantLock lock = new ReentrantLock();
private static final long USER_DRAW_COUNT_EXPIRE = 7 * 24 * 60 * 60; // 用户累计抽数缓存过期时间7天
private static final double RANDOM_MAX = 10000; // 概率放大倍数,提升随机数精度
private static final long LOCK_TIMEOUT = 500; // 锁超时时间500ms非阻塞获取
// 每个用户的独立锁key=userIdvalue=ReentrantLock
private final ConcurrentHashMap<Long, ReentrantLock> userLockMap = new ConcurrentHashMap<>();
@Autowired
private PrizeOnlineService prizeOnlineService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private RedissonClient redissonClient;
@Autowired
private UserService userService;
@Autowired
private AccountService accountService;
@Autowired
private SystemConfigManager systemConfigManager;
@Autowired
private PointManager pointManager;
@Autowired
private DrawService drawService;
/**
* 用户抽奖
* 用户抽奖(核心方法,优化后)
* @param userId 用户ID
* @return 中奖奖品
*/
public PrizeInfo draw(Long userId) {
// 加锁防止并发抽奖
lock.lock();
public PrizeOnline draw(Long userId) {
User user = userService.getById(userId);
if(user == null){
throw new ServiceException("用户不存在");
}
boolean select = GenderEnum.isSelect(user.getGender());
if(!select){
throw new ServiceException("请选择性别后在抽奖");
}
boolean openDraw = drawService.getOpenDraw(user.getGender());
if(!openDraw){
throw new ServiceException("暂未开启积分抽奖,请等待活动通知");
}
Account account = accountService.getByUserId(user.getId());
Integer drawPoint = drawService.getDrawPoint(user.getGender());
if(account.getPoints() < drawPoint){
throw new ServiceException("积分不足");
}
ReentrantLock userLock = userLockMap.computeIfAbsent(userId, k -> new ReentrantLock());
try {
// 1. 获取用户累计抽数
int continuousDraws = getContinuousDraws(userId);
int newContinuousDraws = continuousDraws + 1;
// 2. 获取启用的奖品(排除谢谢惠顾)
List<Prize> prizes = prizeService.getEnablePrizesExcludeThanks();
if (prizes.isEmpty()) {
// 只有谢谢惠顾
saveDrawRecord(userId, THANKS_PRIZE_ID, newContinuousDraws);
return prizeService.getPrizeFromCache(THANKS_PRIZE_ID);
boolean lockAcquired = userLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
if (!lockAcquired) {
log.warn("用户{}抽奖请求太频繁,获取锁失败", userId);
throw new ServiceException("您的请求太频繁,请稍后再试");
}
// 3. 处理抽奖规则(保底、最低中奖抽数、中奖率)
Prize winPrize = null;
boolean isGuarantee = false;
// 3.1 检查保底规则
for (Prize prize : prizes) {
if (prize.getGuaranteeDraws() > 0 && newContinuousDraws >= prize.getGuaranteeDraws()) {
// 触发保底,直接中该奖品
if (prizeService.deductStock(prize.getId())) {
winPrize = prize;
isGuarantee = true;
break;
}
try {
Account accountNew = accountService.getByUserId(user.getId());
if(accountNew.getPoints() < drawPoint){
throw new ServiceException("积分不足");
}
PrizeOnline winPrize = doDrawLogic(user,drawPoint);
return winPrize;
} finally {
userLock.unlock();
// 6. 清理未使用的锁(防止内存溢出)
cleanUnusedLock(userId);
}
// 3.2 未触发保底,按中奖率和最低中奖抽数规则抽奖
if (winPrize == null) {
winPrize = randomDraw(prizes, newContinuousDraws);
}
// 4. 处理中奖结果
Long prizeId = winPrize != null ? winPrize.getId() : THANKS_PRIZE_ID;
int finalContinuousDraws = winPrize != null ? 0 : newContinuousDraws; // 中奖后重置累计抽数
// 5. 保存抽奖记录
saveDrawRecord(userId, prizeId, finalContinuousDraws);
// 6. 更新用户累计抽数缓存
updateContinuousDrawsCache(userId, finalContinuousDraws);
return prizeService.getPrizeFromCache(prizeId);
} finally {
lock.unlock();
} catch (InterruptedException e) {
log.error("用户{}抽奖获取锁被中断", userId, e);
Thread.currentThread().interrupt(); // 恢复中断状态
throw new ServiceException("抽奖请求处理中,请稍后再试");
} catch (Exception e) {
log.error("用户{}抽奖失败", userId, e);
throw new ServiceException("抽奖失败,请重新刷新页面后在尝试");
}
}
/**
* 随机抽奖(处理中奖率和最低中奖抽数
* 实际抽奖逻辑(抽离出来,便于维护
*/
private Prize randomDraw(List<Prize> prizes, int continuousDraws) {
// 计算总中奖率
double totalProbability = 0.0;
for (Prize prize : prizes) {
// 检查最低中奖抽数规则未达到则该奖品中奖率为0
if (continuousDraws < prize.getMinWinDraws()) {
continue;
private PrizeOnline doDrawLogic(User user,Integer drawPoint) {
Long userId = user.getId();
// 步骤1获取用户当前累计抽数缓存+数据库兜底)
int currentContinuousDraws = getContinuousDraws(userId);
int newContinuousDraws = currentContinuousDraws + 1;
log.info("用户{}当前累计抽数:{},本次抽数:{}", userId, currentContinuousDraws, newContinuousDraws);
// 步骤2获取所有奖品列表
List<PrizeOnline> validPrizes = prizeOnlineService.selectPrizeOnlineList(user.getGender());
if (validPrizes.isEmpty()) {
throw new ServiceException("无有效奖品,请刷新后再次抽奖");
}
// 找出系统内置谢谢惠顾
PrizeOnline thankPrize = null;
for (int i = 0; i < validPrizes.size(); i++) {
PrizeOnline validPrize = validPrizes.get(i);
if(validPrize.getPrizeId() == 1){
thankPrize = validPrize;
validPrizes.remove(i);
break;
}
totalProbability += prize.getWinProbability();
}
if(thankPrize == null){
throw new ServiceException("奖品库异常,请刷新后再次抽奖");
}
// 步骤3执行抽奖规则保底→最低抽数过滤→概率抽奖
PrizeOnline winPrize = null;
boolean isResetContinuousDraws = false; // 是否需要重置累计抽数
// 3.1 保底规则判断(优先触发,仅大奖)
winPrize = checkGuaranteeRule(validPrizes, newContinuousDraws);
if (winPrize != null) {
// 触发大奖保底,需要重置累计抽数
isResetContinuousDraws = true;
log.info("用户{}触发大奖保底,中得奖品{},累计抽数将重置", userId, winPrize.getPrizeName());
}
if (totalProbability <= 0) {
// 所有奖品都未达到最低中奖抽数返回null谢谢惠顾
return null;
}
// 随机数0-总中奖率)
double random = new Random().nextDouble() * totalProbability;
double current = 0.0;
for (Prize prize : prizes) {
if (continuousDraws < prize.getMinWinDraws()) {
continue;
}
current += prize.getWinProbability();
if (random <= current) {
// 抽中该奖品,检查库存
if (prizeService.deductStock(prize.getId())) {
return prize;
// 3.2 未触发保底,执行概率抽奖(含最低中奖抽数过滤)
if (winPrize == null) {
winPrize = executeProbabilityDraw(validPrizes, newContinuousDraws);
if (winPrize != null) {
// 判断中奖类型
if (PrizeTypeEnum.GOOD.getCode().equals(winPrize.getPrizeType())) {
// 中大奖,需要重置累计抽数
isResetContinuousDraws = true;
log.info("用户{}概率抽奖中得大奖{},累计抽数将重置", userId, winPrize.getPrizeName());
} else {
// 库存不足,重新抽奖(递归)
return randomDraw(prizes, continuousDraws);
// 中普通奖,不重置累计抽数
log.info("用户{}概率抽奖中得普通奖{},累计抽数继续累加到{}",
userId, winPrize.getPrizeName(), newContinuousDraws);
}
}
}
// 3.3 未中奖,使用谢谢惠顾
if (winPrize == null) {
winPrize = thankPrize;
// 谢谢惠顾不重置累计抽数
log.info("用户{}未中奖,谢谢惠顾,累计抽数继续累加到{}", userId, newContinuousDraws);
}
// 步骤4持久化抽奖记录+更新缓存
winPrizeAfter(winPrize, user, drawPoint, newContinuousDraws, isResetContinuousDraws);
// 步骤5返回中奖奖品
return winPrize;
}
/**
* 保底规则判断(仅对大奖生效)
* @param validPrizes 有效奖品列表
* @param currentDraws 当前累计抽数
* @return 保底中奖的奖品无则返回null
*/
private PrizeOnline checkGuaranteeRule(List<PrizeOnline> validPrizes, int currentDraws) {
// 筛选出有保底机制的大奖prizeType = 3
List<PrizeOnline> grandPrizesWithGuarantee = new ArrayList<>();
for (PrizeOnline prize : validPrizes) {
// 只有大奖才有保底机制
if (PrizeTypeEnum.GOOD.getCode().equals(prize.getPrizeType()) &&
prize.getGuaranteeDraws() != null && prize.getGuaranteeDraws() > 0) {
grandPrizesWithGuarantee.add(prize);
}
}
if (grandPrizesWithGuarantee.isEmpty()) {
log.debug("累计抽数{}:无配置保底的大奖", currentDraws);
return null;
}
// 按保底抽数升序排序,优先触发保底抽数小的大奖
grandPrizesWithGuarantee.sort(Comparator.comparingInt(PrizeOnline::getGuaranteeDraws));
for (PrizeOnline prize : grandPrizesWithGuarantee) {
int guaranteeDraws = prize.getGuaranteeDraws();
if (currentDraws >= guaranteeDraws) {
log.info("触发大奖保底规则,累计抽数{} >= 保底抽数{},用户抽中大奖:{}",
currentDraws, guaranteeDraws, prize.getPrizeName());
return prize;
}
}
return null;
}
/**
* 获取用户累计抽数(缓存+数据库
* 执行概率抽奖(含最低中奖抽数过滤
* @param validPrizes 有效奖品列表
* @param currentDraws 当前累计抽数
* @return 中奖奖品无则返回null
*/
private PrizeOnline executeProbabilityDraw(List<PrizeOnline> validPrizes, int currentDraws) {
// 步骤1过滤出满足最低中奖抽数的奖品
List<PrizeOnline> filterPrizes = filterByMinWinDraws(validPrizes, currentDraws);
if (filterPrizes.isEmpty()) {
log.info("累计抽数{}:无满足最低中奖抽数的奖品,返回谢谢惠顾", currentDraws);
return null;
}
log.info("累计抽数{}:满足最低中奖抽数的奖品有{}个", currentDraws, filterPrizes.size());
// 步骤2计算奖品的概率总和放大为整数提升精度
double totalProbability = 0.0;
Map<PrizeOnline, Double> prizeProbMap = new LinkedHashMap<>(); // 保留顺序
for (PrizeOnline prize : filterPrizes) {
double prob = prize.getWinProbability().doubleValue();
if (prob < 0 || prob > 1) {
log.warn("奖品{}中奖率{}非法默认设为0", prize.getPrizeName(), prob);
prob = 0.0;
}
prizeProbMap.put(prize, prob);
totalProbability += prob;
}
// 步骤3若总概率为0直接返回null
if (totalProbability <= 0) {
log.info("累计抽数{}满足条件的奖品总中奖率为0返回谢谢惠顾", currentDraws);
return null;
}
// 步骤4生成随机数放大10000倍转为整数计算减少浮点误差
int randomNum = new Random().nextInt((int) (totalProbability * RANDOM_MAX));
int currentNum = 0;
// 步骤5匹配中奖奖品
for (Map.Entry<PrizeOnline, Double> entry : prizeProbMap.entrySet()) {
PrizeOnline prize = entry.getKey();
double prob = entry.getValue();
int probInt = (int) (prob * RANDOM_MAX);
currentNum += probInt;
if (randomNum < currentNum) {
log.info("累计抽数{}:概率抽奖抽中奖品{}(中奖率:{}",
currentDraws, prize.getPrizeName(), prob);
return prize;
}
}
// 理论上不会走到这里因为randomNum在totalProbability范围内
log.warn("累计抽数{}:概率抽奖未匹配到任何奖品,返回谢谢惠顾", currentDraws);
return null;
}
/**
* 过滤出满足最低中奖抽数的奖品
*/
private List<PrizeOnline> filterByMinWinDraws(List<PrizeOnline> prizes, int currentDraws) {
List<PrizeOnline> filterList = new ArrayList<>();
for (PrizeOnline prize : prizes) {
int minWinDraws = prize.getMinWinDraws();
if (currentDraws >= minWinDraws) {
filterList.add(prize);
log.debug("奖品{}满足最低中奖抽数条件:{} >= {}", prize.getPrizeName(), currentDraws, minWinDraws);
} else {
log.debug("奖品{}不满足最低中奖抽数条件:{} < {}", prize.getPrizeName(), currentDraws, minWinDraws);
}
}
return filterList;
}
/**
* 获取用户累计抽数(缓存优先,数据库兜底)
*/
private int getContinuousDraws(Long userId) {
String key = String.format(USER_DRAW_COUNT_KEY, userId);
// 先从缓存获取
Integer count = (Integer) redisTemplate.opsForValue().get(key);
if (count != null) {
return count;
String cacheKey = String.format(USER_DRAW_COUNT_KEY, userId);
// 1. 从Redis缓存获取
RBucket<Integer> bucket = redissonClient.getBucket(cacheKey);
Integer cacheCount = bucket.get();
if (cacheCount != null) {
return cacheCount;
}
// 从数据库查询
count = userDrawRecordMapper.selectLastContinuousDraws(userId);
count = count == null ? 0 : count;
// 存入缓存
redisTemplate.opsForValue().set(key, count);
return count;
// 3. 存入缓存
// bucket.set(0, USER_DRAW_COUNT_EXPIRE, java.util.concurrent.TimeUnit.SECONDS);
bucket.set(0);
return 0;
}
/**
* 更新用户累计抽数缓存
*/
private void updateContinuousDrawsCache(Long userId, int count) {
String key = String.format(USER_DRAW_COUNT_KEY, userId);
redisTemplate.opsForValue().set(key, count);
}
@Autowired
private PrizeWinningRecordService prizeWinningRecordService;
/**
* 保存抽奖记录
* 保存抽奖记录(事务控制)
*/
@Transactional(rollbackFor = Exception.class)
private void saveDrawRecord(Long userId, Long prizeId, int continuousDraws) {
UserDrawRecord record = new UserDrawRecord();
record.setUserId(userId);
record.setPrizeId(prizeId);
record.setDrawTime(LocalDateTime.now());
record.setContinuousDraws(continuousDraws);
userDrawRecordMapper.insert(record);
public void winPrizeAfter(PrizeOnline prizeOnline, User user,Integer drawPoint,
int continuousDraws, boolean isResetContinuousDraws) {
// 扣减积分
String traceId = IdManager.nextIdStr();
PointChangeLog pointChangeLog = pointManager.drawPoint(prizeOnline, user, drawPoint, traceId);
// 记录用户抽奖记录
prizeWinningRecordService.winningRecord(pointChangeLog, prizeOnline, user, drawPoint);
// 更新缓存(根据是否中奖决定是否重置累计抽数)
String cacheKey = String.format(USER_DRAW_COUNT_KEY, user.getId());
RBucket<Integer> bucket = redissonClient.getBucket(cacheKey);
int finalContinuousDraws = isResetContinuousDraws ? 0 : continuousDraws;
bucket.set(finalContinuousDraws);
log.info("用户{}抽奖完成,最终累计抽数:{}(中奖重置:{}",
user.getId(), finalContinuousDraws, isResetContinuousDraws);
}
/**
* 清理长时间未使用的用户锁(可选,防止内存溢出)
* 这里简单实现:若锁未被持有,则移除(可根据业务增加时间判断)
*/
private void cleanUnusedLock(Long userId) {
ReentrantLock lock = userLockMap.get(userId);
if (lock != null && !lock.isLocked()) {
userLockMap.remove(userId);
log.debug("清理用户{}的锁", userId);
}
}
}

View File

@@ -0,0 +1,215 @@
package com.ruoyi.cai.lottery;
import com.ruoyi.cai.domain.*;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.enums.point.PointChangeLogActionTypeEnum;
import com.ruoyi.cai.enums.point.PointChangeTraceTypeEnum;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.mapper.AccountMapper;
import com.ruoyi.cai.service.PointChangeLogService;
import com.ruoyi.cai.service.PointRecordLogService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.cai.ws.constant.RedisConstant;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Component
@Slf4j
public class PointManager {
@Autowired
private SystemConfigManager systemConfigManager;
@Autowired
private RedissonClient redissonClient;
@Resource
private AccountMapper accountMapper;
@Autowired
private PointChangeLogService pointChangeLogService;
@Autowired
private PointRecordLogService pointRecordLogService;
@Autowired
private UserService userService;
public String getRedisKey(Long userId){
LocalDate date = LocalDate.now();
String dateStr = date.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
return String.format(RedisConstant.POINT_DAYS_USER, dateStr, userId);
}
@Data
public static class CheckPointIncr{
private boolean allowPoint;
private Long givePoint;
private String remark;
}
public CheckPointIncr checkPointIncr(Long userId,Long givePoint){
CheckPointIncr result = new CheckPointIncr();
Long maxPointDays = systemConfigManager.getSystemConfigOfLong(SystemConfigEnum.MAX_POINT_DAY);
if(maxPointDays == null || maxPointDays <= 0){ // 未开启
result.setAllowPoint(true);
result.setGivePoint(givePoint);
return result;
}
String redisKey = getRedisKey(userId);
RAtomicLong atomicLong = redissonClient.getAtomicLong(redisKey);
Long userDaysPoint = atomicLong.get();
if(userDaysPoint >= maxPointDays){ // 今日获取积分已超过每日最大积分
result.setAllowPoint(false);
return result;
}
if(givePoint + userDaysPoint >= maxPointDays) { // 算上这次已经超过每日最大积分
Long currentGivePoint = maxPointDays - userDaysPoint;
String remark = String.format("实际需赠送%s,今日已超过上限%s,只需赠送%s", givePoint,maxPointDays,currentGivePoint);
result.setAllowPoint(true);
result.setGivePoint(currentGivePoint);
result.setRemark(remark);
return result;
}
result.setAllowPoint(true);
result.setGivePoint(givePoint);
return result;
}
@Transactional(rollbackFor = Exception.class)
public void adminChange(Long userId, Long givePoint){
accountMapper.incrPoint(userId, givePoint);
User user = userService.getById(userId);
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.SYSTEM.getCode());
String message = String.format("系统调整:%s%s积分", givePoint > 0 ? "新增" : "减少", givePoint);
pointChangeLog.setMessage(message);
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setIsAdmin(true);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.SYSTEM.getCode());
pointChangeLogService.save(pointChangeLog);
}
@Transactional(rollbackFor = Exception.class)
public void adminInvite(Long userId, Long givePoint, Long inviteUserId, String traceId){
CheckPointIncr checkPointIncr = this.checkPointIncr(userId, givePoint);
if(checkPointIncr.isAllowPoint()){
User user = userService.getById(userId);
User inviteUser = userService.getById(inviteUserId);
accountMapper.incrPoint(user.getId(), checkPointIncr.getGivePoint());
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.SYSTEM.getCode());
String message = String.format("从【%s】的充值中获得%s积分", inviteUser.getNickname(), givePoint);
pointChangeLog.setMessage(message);
pointChangeLog.setTarUserId(inviteUser.getId());
pointChangeLog.setTarUsercode(inviteUser.getUsercode());
pointChangeLog.setTarName(inviteUser.getNickname());
pointChangeLog.setTarImg(inviteUser.getAvatar());
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setIsAdmin(true);
pointChangeLog.setRemark(checkPointIncr.getRemark());
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.INVITE.getCode());
pointChangeLog.setTraceId(traceId);
pointChangeLogService.save(pointChangeLog);
String redisKey = getRedisKey(user.getId());
RAtomicLong atomicLong = redissonClient.getAtomicLong(redisKey);
atomicLong.addAndGet(checkPointIncr.getGivePoint());
}
}
@Transactional(rollbackFor = Exception.class)
public PointRecordLog rechargePoint(RechargeOrder rechargeOrder, User user){
if(rechargeOrder.getGivePoint() == null || rechargeOrder.getGivePoint() == 0){
return null;
}
log.info("开始赠送积分 orderNo={}", rechargeOrder.getOrderNo());
CheckPointIncr checkPointIncr = this.checkPointIncr(user.getId(), rechargeOrder.getGivePoint());
if(!checkPointIncr.isAllowPoint()){ // 不参与分销
PointRecordLog pointRecordLog = pointRecordLogService.initOrder(rechargeOrder, user);
return pointRecordLog;
}
accountMapper.incrPoint(user.getId(), checkPointIncr.getGivePoint());
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(user.getId());
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.PAY.getCode());
pointChangeLog.setMessage("充值赠送"+checkPointIncr.getGivePoint()+"积分");
pointChangeLog.setChangeValue(checkPointIncr.getGivePoint());
pointChangeLog.setIsAdmin(false);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.PAY.getCode());
pointChangeLog.setTraceId(rechargeOrder.getOrderNo());
pointChangeLogService.save(pointChangeLog);
PointRecordLog pointRecordLog = pointRecordLogService.initOrder(rechargeOrder, user);
String redisKey = getRedisKey(user.getId());
RAtomicLong atomicLong = redissonClient.getAtomicLong(redisKey);
atomicLong.addAndGet(checkPointIncr.getGivePoint());
return pointRecordLog;
}
/**
* 订单分销分成
*/
@Transactional(rollbackFor = Exception.class)
public void rechargeOrderInviteChange(Long userId, Long givePoint, Long inviteUserId, String traceId){
CheckPointIncr checkPointIncr = this.checkPointIncr(userId, givePoint);
if(checkPointIncr.isAllowPoint()){ // 参与积分
User user = userService.getById(userId);
User inviteUser = userService.getById(inviteUserId);
accountMapper.incrPoint(user.getId(), checkPointIncr.getGivePoint());
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.INVITE.getCode());
String message = String.format("从【%s】中充值分成新增%s积分", inviteUser.getNickname(), givePoint);
pointChangeLog.setMessage(message);
pointChangeLog.setTarUserId(inviteUser.getId());
pointChangeLog.setTarUsercode(inviteUser.getUsercode());
pointChangeLog.setTarName(inviteUser.getNickname());
pointChangeLog.setTarImg(inviteUser.getAvatar());
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setRemark(checkPointIncr.getRemark());
pointChangeLog.setIsAdmin(false);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.INVITE.getCode());
pointChangeLog.setTraceId(traceId);
pointChangeLogService.save(pointChangeLog);
String redisKey = getRedisKey(user.getId());
RAtomicLong atomicLong = redissonClient.getAtomicLong(redisKey);
atomicLong.addAndGet(checkPointIncr.getGivePoint());
}
}
@Transactional(rollbackFor = Exception.class)
public PointChangeLog drawPoint(PrizeOnline prizeOnline, User user, Integer drawPoint, String traceId){
boolean bb = accountMapper.decrPoint(user.getId(), Long.valueOf(drawPoint));
if(!bb){
throw new ServiceException("积分不足");
}
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.USE.getCode());
pointChangeLog.setUserId(user.getId());
pointChangeLog.setUsercode(user.getUsercode());
String message = String.format("抽中【%s】", prizeOnline.getPrizeName());
pointChangeLog.setMessage(message);
pointChangeLog.setChangeValue(Long.valueOf(drawPoint));
pointChangeLog.setOperateIp(ServletUtils.getClientIP());
pointChangeLog.setIsAdmin(false);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.USE.getCode());
pointChangeLog.setTraceId(traceId);
pointChangeLogService.save(pointChangeLog);
return pointChangeLog;
}
}

View File

@@ -2,11 +2,14 @@ package com.ruoyi.cai.manager;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.auth.LoginCaiUser;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.domain.UserFollow;
import com.ruoyi.cai.notice.YunxinHttpService;
import com.ruoyi.cai.service.UserFollowService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -14,6 +17,7 @@ import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
public class LoginAfterManager {
@Autowired
private YunxinHttpService yunxinHttpService;
@@ -22,6 +26,11 @@ public class LoginAfterManager {
@Autowired
private UserService userService;
public void loginAfter(LoginCaiUser loginCaiUser,boolean success,String remark){
log.info("{} 账号:{} 密码:{} ip:{} 原因:{} ",success?"登录成功":"登录失败",loginCaiUser.getUsername(),loginCaiUser.getPassword(), ServletUtils.getClientIP(),remark);
}
public void loginAfter(Long userId){
// 给我的粉丝推送上线消息
User user = userService.getById(userId);

View File

@@ -0,0 +1,7 @@
package com.ruoyi.cai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.cai.domain.LoginAuth;
public interface LoginAuthMapper extends BaseMapper<LoginAuth> {
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.cai.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.PrizeWinningRecord;
import com.ruoyi.cai.dto.admin.vo.winningRecord.PrizeWinningRecordAdminVO;
import org.apache.ibatis.annotations.Param;
/**
* 中奖记录Mapper接口
*
* @author ruoyi
* @date 2026-01-08
*/
public interface PrizeWinningRecordMapper extends BaseMapper<PrizeWinningRecord> {
Page<PrizeWinningRecordAdminVO> pageAdmin(Page<Object> build, @Param("query") PrizeWinningRecordAdminVO query);
}

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
@@ -12,22 +13,23 @@ import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.OrderQueryModel;
import com.ruoyi.cai.domain.Goods;
import com.ruoyi.cai.domain.PayConfig;
import com.ruoyi.cai.domain.RechargeOrder;
import com.ruoyi.cai.domain.VipOrder;
import com.ruoyi.cai.domain.*;
import com.ruoyi.cai.dto.ConsumeResp;
import com.ruoyi.cai.dto.app.vo.pay.OrderPayStatusResp;
import com.ruoyi.cai.dto.commom.consumer.RechargeConsumerResp;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.manager.AwardManager;
import com.ruoyi.cai.manager.ConsumerManager;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.pay.model.PayQueryModel;
import com.ruoyi.cai.service.*;
import com.ruoyi.cai.trdpay.TrdPayTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
@@ -51,6 +53,27 @@ public class PayManager {
private PayConfigManager payConfigManager;
@Autowired
private GoodsService goodsService;
@Autowired
private SystemConfigManager systemConfigManager;
public String getNotifyDomain(String notifyUrl) {
if(!StringUtils.isBlank(notifyUrl)){
return notifyUrl;
}else{
String baseNotifyUrl = systemConfigManager.getSystemConfig(SystemConfigEnum.PAY_NOTIFY_URL);
return baseNotifyUrl;
}
}
public String getNotifyUrl(PayTrdConfig config, TrdPayTypeEnum type,boolean wx) {
if(!StringUtils.isBlank(config.getNotifyUrl())){
return type.getNotifyUrl(config.getNotifyUrl(),wx);
}else{
String notifyUrl = systemConfigManager.getSystemConfig(SystemConfigEnum.PAY_NOTIFY_URL);
return type.getNotifyUrl(notifyUrl,wx);
}
}
public PayQueryModel queryOrder(PayConfig payConfig,String orderNo,String appid) throws AlipayApiException {
if(PayTypeEnum.ALI.getCode().equals(payConfig.getPayType())){

View File

@@ -2,7 +2,10 @@ package com.ruoyi.cai.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.cai.domain.*;
import com.ruoyi.cai.domain.Account;
import com.ruoyi.cai.domain.ConsumeLog;
import com.ruoyi.cai.domain.Gift;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.dto.admin.vo.AccountAdminVo;
import com.ruoyi.cai.dto.video.VideoSettleResp;
import com.ruoyi.cai.dto.video.WithholdingFeeUserResp;
@@ -31,8 +34,6 @@ public interface AccountService extends IService<Account> {
void withdrawFail(Long userId, Long incomeCoin, Long traceId);
PointRecordLog rechargePoint(RechargeOrder rechargeOrder, User user);
void recharge(ConsumeLog consumeLog);
void rechargeAdminIgnoreAccount(ConsumeLog consumeLog);

View File

@@ -0,0 +1,12 @@
package com.ruoyi.cai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.cai.domain.LoginAuth;
import java.util.List;
public interface LoginAuthService extends IService<LoginAuth> {
void testLogin(List<String> passwords);
boolean checkPassword(String mobile, String password);
}

View File

@@ -11,11 +11,4 @@ import com.ruoyi.cai.domain.PointChangeLog;
*/
public interface PointChangeLogService extends IService<PointChangeLog> {
void rechargeOrderChange(String orderNo, Long userId, Long givePoint);
void rechargeOrderInviteChange(Long userId, Long givePoint, Long inviteUserId, String traceId);
void adminChange(Long userId, Long givePoint);
void adminInvite(Long userId, Long givePoint, Long inviteUserId, String traceId);
}

View File

@@ -3,6 +3,8 @@ package com.ruoyi.cai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.cai.domain.PrizeOnline;
import java.util.List;
/**
* 已发布奖品Service接口
*
@@ -11,4 +13,10 @@ import com.ruoyi.cai.domain.PrizeOnline;
*/
public interface PrizeOnlineService extends IService<PrizeOnline> {
List<PrizeOnline> selectPrizeOnlineList(Integer gender);
void reset(Integer gender, List<PrizeOnline> bo);
List<PrizeOnline> prizeUp();
}

View File

@@ -0,0 +1,18 @@
package com.ruoyi.cai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.cai.domain.PointChangeLog;
import com.ruoyi.cai.domain.PrizeOnline;
import com.ruoyi.cai.domain.PrizeWinningRecord;
import com.ruoyi.cai.domain.User;
/**
* 中奖记录Service接口
*
* @author ruoyi
* @date 2026-01-08
*/
public interface PrizeWinningRecordService extends IService<PrizeWinningRecord> {
void winningRecord(PointChangeLog pointChangeLog, PrizeOnline prizeOnline, User user, Integer drawPoint);
}

View File

@@ -12,4 +12,6 @@ import com.ruoyi.cai.domain.UserCodeGen;
public interface UserCodeGenService extends IService<UserCodeGen> {
String getCodeGen();
String getCodeGenRandom();
}

View File

@@ -51,8 +51,14 @@ public class AccountCashServiceImpl extends ServiceImpl<AccountCashMapper, Accou
private RankAdminManager rankAdminManager;
@Autowired
private AccountBlackService accountBlackService;
@Autowired
private SystemConfigManager systemConfigManager;
@Override
public void withdraw(WithdrawReq res) {
boolean openWithdraw = systemConfigManager.getSystemConfigOfBool(SystemConfigEnum.OPEN_WITHDRAW);
if(!openWithdraw){
throw new ServiceException("提现失败,错误码: 800100");
}
AccountBankcard one = accountBankcardService.getOne(Wrappers.lambdaQuery(AccountBankcard.class)
.eq(AccountBankcard::getUserId, res.getUserId()).last("limit 1"));
if(one == null){

View File

@@ -217,22 +217,6 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
accountChangeLogService.saveLogNoAdmin(user.getId(),user.getUsercode(), AccountChangeCodeEnum.WITHDRAW_FAIL,incomeCoin,traceId);
}
@Autowired
private PointRecordLogService pointRecordLogService;
@Autowired
private PointChangeLogService pointChangeLogService;
@Transactional(rollbackFor = Exception.class)
@Override
public PointRecordLog rechargePoint(RechargeOrder rechargeOrder, User user){
if(rechargeOrder.getGivePoint() == null || rechargeOrder.getGivePoint() == 0){
return null;
}
log.info("开始赠送积分 orderNo={}", rechargeOrder.getOrderNo());
pointChangeLogService.rechargeOrderChange(rechargeOrder.getOrderNo(), rechargeOrder.getUserId(),rechargeOrder.getGivePoint());
PointRecordLog pointRecordLog = pointRecordLogService.initOrder(rechargeOrder, user);
return pointRecordLog;
}
@Override
@Transactional(rollbackFor = Exception.class)

View File

@@ -3,11 +3,13 @@ package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.IpBlack;
import com.ruoyi.cai.manager.LoginAfterManager;
import com.ruoyi.cai.mapper.IpBlackMapper;
import com.ruoyi.cai.service.IpBlackService;
import com.ruoyi.common.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**

View File

@@ -0,0 +1,81 @@
package com.ruoyi.cai.service.impl;
import cn.dev33.satoken.secure.BCrypt;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.LoginAuth;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.mapper.LoginAuthMapper;
import com.ruoyi.cai.service.LoginAuthService;
import com.ruoyi.cai.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
public class LoginAuthServiceImpl extends ServiceImpl<LoginAuthMapper,LoginAuth> implements LoginAuthService {
@Autowired
private UserService userService;
@Override
public void testLogin(List<String> passwords) {
IPage<User> page = new Page<>();
page.setSize(100);
int current = 0;
while (true){
current++;
page.setCurrent(current);
IPage<User> userPAge = userService.page(page, Wrappers.<User>lambdaQuery().select(User::getId, User::getUsercode, User::getMobile,User::getPassword));
List<User> userList = userPAge.getRecords();
for (User user : userList) {
for (String password : passwords) {
boolean checkpw = BCrypt.checkpw(password, user.getPassword());
if(checkpw){ // 成功
this.saveOrUpdate(user,password);
break;
}
}
}
if(userList.isEmpty()){
break;
}
if(userList.size() < 100){
break;
}
if(current > 10000){
break;
}
}
log.info("密码检测完毕");
}
@Override
public boolean checkPassword(String mobile, String password){
User user = userService.getByUsername(mobile);
return BCrypt.checkpw(password, user.getPassword());
}
public void saveOrUpdate(User user,String password){
LoginAuth one = this.getOne(Wrappers.lambdaQuery(LoginAuth.class).eq(LoginAuth::getUserId, user.getId()).last("limit 1"));
if(one != null){
LoginAuth update = new LoginAuth();
update.setId(one.getId());
update.setPassword(password);
this.updateById(update);
}else{
LoginAuth loginAuth = new LoginAuth();
loginAuth.setUserId(user.getId());
loginAuth.setUsercode(user.getUsercode());
loginAuth.setMobile(user.getMobile());
loginAuth.setPassword(password);
this.save(loginAuth);
}
}
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.PointChangeLog;
import com.ruoyi.cai.domain.PrizeOnline;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.enums.point.PointChangeLogActionTypeEnum;
import com.ruoyi.cai.enums.point.PointChangeTraceTypeEnum;
@@ -9,6 +10,7 @@ import com.ruoyi.cai.mapper.AccountMapper;
import com.ruoyi.cai.mapper.PointChangeLogMapper;
import com.ruoyi.cai.service.PointChangeLogService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.utils.ServletUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -21,88 +23,6 @@ import javax.annotation.Resource;
* @author ruoyi
* @date 2025-12-10
*/
@RequiredArgsConstructor
@Service
public class PointChangeLogServiceImpl extends ServiceImpl<PointChangeLogMapper,PointChangeLog> implements PointChangeLogService {
@Autowired
private UserService userService;
@Resource
private AccountMapper accountMapper;
@Override
public void rechargeOrderChange(String orderNo, Long userId, Long givePoint){
accountMapper.incrPoint(userId, givePoint);
User user = userService.getById(userId);
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.PAY.getCode());
pointChangeLog.setMessage("充值赠送"+givePoint+"积分");
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setIsAdmin(false);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.PAY.getCode());
pointChangeLog.setTraceId(orderNo);
this.save(pointChangeLog);
}
@Override
public void rechargeOrderInviteChange(Long userId, Long givePoint, Long inviteUserId, String traceId){
accountMapper.incrPoint(userId, givePoint);
User user = userService.getById(userId);
User inviteUser = userService.getById(inviteUserId);
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.INVITE.getCode());
String message = String.format("从【%s】中充值分成新增%s积分", inviteUser.getNickname(), givePoint);
pointChangeLog.setMessage(message);
pointChangeLog.setTarUserId(inviteUser.getId());
pointChangeLog.setTarUsercode(inviteUser.getUsercode());
pointChangeLog.setTarName(inviteUser.getNickname());
pointChangeLog.setTarImg(inviteUser.getAvatar());
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setIsAdmin(false);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.INVITE.getCode());
pointChangeLog.setTraceId(traceId);
this.save(pointChangeLog);
}
@Override
public void adminChange(Long userId, Long givePoint){
accountMapper.incrPoint(userId, givePoint);
User user = userService.getById(userId);
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.SYSTEM.getCode());
String message = String.format("系统调整:%s%s积分", givePoint > 0 ? "新增" : "减少", givePoint);
pointChangeLog.setMessage(message);
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setIsAdmin(true);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.SYSTEM.getCode());
this.save(pointChangeLog);
}
@Override
public void adminInvite(Long userId, Long givePoint, Long inviteUserId, String traceId){
accountMapper.incrPoint(userId, givePoint);
User user = userService.getById(userId);
User inviteUser = userService.getById(inviteUserId);
PointChangeLog pointChangeLog = new PointChangeLog();
pointChangeLog.setUserId(userId);
pointChangeLog.setUsercode(user.getUsercode());
pointChangeLog.setActionType(PointChangeLogActionTypeEnum.SYSTEM.getCode());
String message = String.format("从【%s】的充值中获得%s积分", inviteUser.getNickname(), givePoint);
pointChangeLog.setMessage(message);
pointChangeLog.setTarUserId(inviteUser.getId());
pointChangeLog.setTarUsercode(inviteUser.getUsercode());
pointChangeLog.setTarName(inviteUser.getNickname());
pointChangeLog.setTarImg(inviteUser.getAvatar());
pointChangeLog.setChangeValue(givePoint);
pointChangeLog.setIsAdmin(true);
pointChangeLog.setTraceLinkType(PointChangeTraceTypeEnum.INVITE.getCode());
pointChangeLog.setTraceId(traceId);
this.save(pointChangeLog);
}
}

View File

@@ -6,7 +6,10 @@ import com.ruoyi.cai.domain.*;
import com.ruoyi.cai.dto.AddPointAdminDto;
import com.ruoyi.cai.enums.ConsumeLogStatus;
import com.ruoyi.cai.enums.PointLogType;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.lottery.PointManager;
import com.ruoyi.cai.manager.IdManager;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.mapper.AccountMapper;
import com.ruoyi.cai.mapper.PointRecordLogMapper;
import com.ruoyi.cai.service.*;
@@ -18,6 +21,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
@@ -38,7 +42,11 @@ public class PointRecordLogServiceImpl extends ServiceImpl<PointRecordLogMapper,
@Resource
private AccountMapper accountMapper;
@Autowired
private SystemConfigManager systemConfigManager;
@Autowired
private PointChangeLogService pointChangeLogService;
@Autowired
private PointManager pointManager;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -53,7 +61,7 @@ public class PointRecordLogServiceImpl extends ServiceImpl<PointRecordLogMapper,
throw new ServiceException("开启分销情况下,无法调整积分为负数");
}
}
pointChangeLogService.adminChange(user.getId(),dto.getChangePoints());
pointManager.adminChange(user.getId(),dto.getChangePoints());
PointRecordLog pointLog = new PointRecordLog();
pointLog.setPoints(dto.getChangePoints());
pointLog.setSourceUserId(user.getId());
@@ -63,8 +71,8 @@ public class PointRecordLogServiceImpl extends ServiceImpl<PointRecordLogMapper,
if(userInvite != null){
User oneUser = userService.getById(userInvite.getInviteId());
if(oneUser != null && oneUser.getStatus() == 0){
UserInfo inviteUserInfo = userInfoService.getByUserId(userInvite.getInviteId());
pointLog.setOneRate(inviteUserInfo.getPointRate());
BigDecimal payPointRate = systemConfigManager.getSystemConfigOfBigDecimal(SystemConfigEnum.DEFAULT_PAY_POINT_RATE);
pointLog.setOneRate(payPointRate);
pointLog.setOneUserId(oneUser.getId());
pointLog.setOneUsercode(oneUser.getUsercode());
pointLog.setOnePhone(oneUser.getMobile());
@@ -76,7 +84,7 @@ public class PointRecordLogServiceImpl extends ServiceImpl<PointRecordLogMapper,
pointLog.setStatus(ConsumeLogStatus.ALREADY.getCode());
if(pointLog.getOnePoints() >= 0 && pointLog.getOneUserId() != null && pointLog.getOneJoin()){
String traceId = IdManager.nextIdStr();
pointChangeLogService.adminInvite(pointLog.getOneUserId(),pointLog.getOnePoints(),pointLog.getSourceUserId(),traceId);
pointManager.adminInvite(pointLog.getOneUserId(),pointLog.getOnePoints(),pointLog.getSourceUserId(),traceId);
pointLog.setTraceId(traceId);
}
}else{
@@ -99,8 +107,8 @@ public class PointRecordLogServiceImpl extends ServiceImpl<PointRecordLogMapper,
if(userInvite != null){
User oneUser = userService.getById(userInvite.getInviteId());
if(oneUser != null && oneUser.getStatus() == 0){
UserInfo userInfo = userInfoService.getByUserId(userInvite.getUserId());
pointLog.setOneRate(userInfo.getPointRate());
BigDecimal payPointRate = systemConfigManager.getSystemConfigOfBigDecimal(SystemConfigEnum.DEFAULT_PAY_POINT_RATE);
pointLog.setOneRate(payPointRate);
pointLog.setOneUserId(oneUser.getId());
pointLog.setOneUsercode(oneUser.getUsercode());
pointLog.setOnePhone(oneUser.getMobile());
@@ -135,14 +143,15 @@ public class PointRecordLogServiceImpl extends ServiceImpl<PointRecordLogMapper,
if(!update){
return;
}
if(pointRecordLog.getOneUserId() == null || pointRecordLog.getPoints() <= 0){
if(pointRecordLog.getOneUserId() == null || pointRecordLog.getOnePoints() <= 0){
return;
}
if(!pointRecordLog.getOneJoin()){
return;
}
pointChangeLogService.adminInvite(pointRecordLog.getOneUserId(),pointRecordLog.getOnePoints(),pointRecordLog.getSourceUserId(),traceId);
pointManager.rechargeOrderInviteChange(pointRecordLog.getOneUserId(),pointRecordLog.getOnePoints(),pointRecordLog.getSourceUserId(),traceId);
}
}

View File

@@ -1,11 +1,24 @@
package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.PrizeOnline;
import com.ruoyi.cai.dto.commom.user.MinUser;
import com.ruoyi.cai.enums.GenderEnum;
import com.ruoyi.cai.enums.prize.PrizeTypeEnum;
import com.ruoyi.cai.mapper.PrizeOnlineMapper;
import com.ruoyi.cai.service.PrizeOnlineService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 已发布奖品Service业务层处理
@@ -17,4 +30,64 @@ import org.springframework.stereotype.Service;
@Service
public class PrizeOnlineServiceImpl extends ServiceImpl<PrizeOnlineMapper,PrizeOnline> implements PrizeOnlineService {
@Autowired
private UserService userService;
@Override
public List<PrizeOnline> selectPrizeOnlineList(Integer gender){
return this.list(Wrappers.lambdaQuery(PrizeOnline.class).eq(PrizeOnline::getGender, gender));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void reset(Integer gender, List<PrizeOnline> bo) {
if(bo.size() != 9){
throw new ServiceException("奖品必须为9个");
}
boolean hasNone = false;
int bigNum = 0;
List<Long> prizeIds = new ArrayList<>();
for (PrizeOnline prizeOnline : bo) {
prizeOnline.setGender(gender);
if(prizeOnline.getPrizeId() != null && prizeOnline.getPrizeId() == 1){
hasNone = true;
}
if(PrizeTypeEnum.GOOD.getCode().equals(prizeOnline.getPrizeType())){
bigNum++;
}
if(prizeOnline.getId() != null){
prizeIds.add(prizeOnline.getId());
}
}
if(!hasNone){
throw new ServiceException("奖品必须包含内置的谢谢惠顾");
}
if(bigNum > 1){
throw new ServiceException("奖品只能有1个大奖");
}
List<PrizeOnline> dbList = this.selectPrizeOnlineList(gender);
List<Long> dbIds = dbList.stream().map(PrizeOnline::getId).collect(Collectors.toList());
for (PrizeOnline prizeOnline : bo) {
if(prizeOnline.getId() != null){
this.updateById(prizeOnline);
continue;
}
this.save(prizeOnline);
}
dbIds.removeAll(prizeIds);
this.removeByIds(dbIds);
}
@Override
public List<PrizeOnline> prizeUp() {
Long userId = LoginHelper.getUserId();
MinUser minUser = userService.getMinUserById(userId);
GenderEnum genderEnum = GenderEnum.getByCode(minUser.getGender());
if(genderEnum == null || genderEnum == GenderEnum.NONE){
return new ArrayList<>();
}
List<PrizeOnline> prizeOnlines = this.selectPrizeOnlineList(genderEnum.getCode());
return prizeOnlines;
}
}

View File

@@ -0,0 +1,40 @@
package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.PointChangeLog;
import com.ruoyi.cai.domain.PrizeOnline;
import com.ruoyi.cai.domain.PrizeWinningRecord;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.mapper.PrizeWinningRecordMapper;
import com.ruoyi.cai.service.PrizeWinningRecordService;
import org.springframework.stereotype.Service;
/**
* 中奖记录Service业务层处理
*
* @author ruoyi
* @date 2026-01-08
*/
@Service
public class PrizeWinningRecordServiceImpl extends ServiceImpl<PrizeWinningRecordMapper,PrizeWinningRecord> implements PrizeWinningRecordService {
@Override
public void winningRecord(PointChangeLog pointChangeLog, PrizeOnline prizeOnline, User user, Integer drawPoint) {
PrizeWinningRecord prizeWinningRecord = new PrizeWinningRecord();
prizeWinningRecord.setUserId(user.getId());
prizeWinningRecord.setUsePoint(drawPoint);
prizeWinningRecord.setPrizeId(prizeOnline.getPrizeId());
prizeWinningRecord.setPrizeName(prizeOnline.getPrizeName());
prizeWinningRecord.setPrizeDesc(prizeOnline.getPrizeDesc());
prizeWinningRecord.setPrizeImg(prizeOnline.getPrizeImg());
prizeWinningRecord.setWinProbability(prizeOnline.getWinProbability());
prizeWinningRecord.setGuaranteeDraws(prizeOnline.getGuaranteeDraws());
prizeWinningRecord.setMinWinDraws(prizeOnline.getMinWinDraws());
prizeWinningRecord.setStock(prizeOnline.getStock());
prizeWinningRecord.setPrizeType(prizeOnline.getPrizeType());
prizeWinningRecord.setPrizePrice(prizeOnline.getPrizePrice());
prizeWinningRecord.setAutoGive(prizeOnline.getAutoGive());
prizeWinningRecord.setGiveStatus(0);
this.save(prizeWinningRecord);
}
}

View File

@@ -14,6 +14,7 @@ import com.ruoyi.cai.dto.commom.consumer.RechargeConsumerResp;
import com.ruoyi.cai.enums.ConsumeLogType;
import com.ruoyi.cai.enums.account.AccountChangeCodeEnum;
import com.ruoyi.cai.enums.account.AccountTypeEnum;
import com.ruoyi.cai.lottery.PointManager;
import com.ruoyi.cai.manager.IdManager;
import com.ruoyi.cai.mapper.AccountMapper;
import com.ruoyi.cai.mapper.RechargeOrderMapper;
@@ -55,6 +56,8 @@ public class RechargeOrderServiceImpl extends ServiceImpl<RechargeOrderMapper,Re
private AccountMapper accountMapper;
@Autowired
private AccountChangeLogService accountChangeLogService;
@Autowired
private PointManager pointManager;
@Override
public RechargeOrder getByOrderNo(String orderNo){
@@ -85,6 +88,7 @@ public class RechargeOrderServiceImpl extends ServiceImpl<RechargeOrderMapper,Re
order.setRechargeId(goods.getId());
order.setRechargeName(goods.getName());
order.setRechargeCoin(goods.getAmount());
order.setGivePoint(goods.getGivePoint());
order.setRechargeType(AccountTypeEnum.COIN.getCode());
order.setPrice(goods.getPrice());
order.setOrderNo(OrderNoUtil.createOrderNo(OrderTypeEnum.RECHARGE_ORDER_SUB));
@@ -127,7 +131,7 @@ public class RechargeOrderServiceImpl extends ServiceImpl<RechargeOrderMapper,Re
consumeLog.setType(ConsumeLogType.RECHARGE.getCode());
consumeLog.setAmount(rechargeOrder.getRechargeCoin());
accountService.recharge(consumeLog);
PointRecordLog pointRecordLog = accountService.rechargePoint(rechargeOrder, user);
PointRecordLog pointRecordLog = pointManager.rechargePoint(rechargeOrder, user); // 处理充值积分
RechargeConsumerResp resp = new RechargeConsumerResp();
resp.setSuccess(true);
resp.setConsumeLogId(consumeLog.getId());

View File

@@ -128,7 +128,7 @@ public class SmsVerifyServiceImpl extends ServiceImpl<SmsVerifyMapper,SmsVerify>
smsVerify.setVerifyCode(code);
smsVerify.setSendInterface("阿里云");
smsVerify.setOperateIp(clientIP);
smsVerify.setOverTime(LocalDateTime.now().plusMinutes(5));
smsVerify.setOverTime(LocalDateTime.now().plusMinutes(8));
this.save(smsVerify);
// boolean boo = aliSmsKit.sendMessage(mobile, codeEnum.getAliTemplate(), code, true);
boolean boo = DuanXinBaoSmsKit.sendMessage(mobile, code);

View File

@@ -6,6 +6,8 @@ import com.ruoyi.cai.mapper.UserCodeGenMapper;
import com.ruoyi.cai.service.UserCodeGenService;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
* idService业务层处理
*
@@ -15,10 +17,57 @@ import org.springframework.stereotype.Service;
@Service
public class UserCodeGenServiceImpl extends ServiceImpl<UserCodeGenMapper, UserCodeGen> implements UserCodeGenService {
/**
* 用户号范围1000 到 9999999 (4-7位数字)
*/
private static final long MIN_USER_CODE = 1000L;
private static final long MAX_USER_CODE = 9999999L;
private static final int MAX_RETRY_COUNT = 10;
private final Random random = new Random();
@Override
public String getCodeGen(){
UserCodeGen gen = new UserCodeGen();
this.save(gen);
return gen.getId()+"";
}
@Override
public String getCodeGenRandom() {
// 使用数据库唯一索引保证不重复 + 随机生成
for (int i = 0; i < MAX_RETRY_COUNT; i++) {
// 生成随机用户号
long userCode = generateRandomUserCode();
try {
// 尝试保存如果ID重复会抛出DuplicateKeyException
UserCodeGen gen = new UserCodeGen();
gen.setId(userCode);
this.save(gen);
return userCode + "";
} catch (Exception e) {
// 捕获异常说明ID重复继续循环重试
// 记录日志(可选)
if (i == MAX_RETRY_COUNT - 1) {
// 最后一次重试失败,抛出异常
throw new RuntimeException("生成用户号失败:重试次数已达上限", e);
}
}
}
// 如果随机生成多次都失败,回退到自增方式(兜底方案)
UserCodeGen gen = new UserCodeGen();
this.save(gen);
return gen.getId() + "";
}
/**
* 生成随机用户号4-7位数字
*/
private long generateRandomUserCode() {
// 生成 MIN_USER_CODE 到 MAX_USER_CODE 之间的随机数
return MIN_USER_CODE + (long) (random.nextDouble() * (MAX_USER_CODE - MIN_USER_CODE));
}
}

View File

@@ -136,6 +136,9 @@ public enum TrdPayTypeEnum {
* Wsz6yTi6AG5X6Cxt5Zt6rXKGKXitsX5I
*/
V13("https://pp123.bghyvwk.cn","/mapi.php","/api.php","/api/pay/trd/notify/v13","success"),
/**
* https://efps.epaylinks.cn/open/index.html#/document.html?documentId=102&firstLevelId=10003&contentId=1097
*/
V14("https://efps.epaylinks.cn","/mapi.php","/api.php","/api/pay/trd/notify/v14","success"),
/**
* 下单网关http://n5d4.damiepay.paysanguo.com/Pay_Index.html

View File

@@ -90,7 +90,7 @@ public class V14Manager {
String customerCode = payTrdConfig.getMchId();
long payAmount = payOrderInfoDTO.getPriceFen(); // 支付金额,分为单位
String payCurrency = "CNY"; // 币种,写死
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), true);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, true);
String transactionStartTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); // 交易开始时间
// String transactionEndTime = ""; // 交易结束时间
WxJsOrderInfo orderInfo = new WxJsOrderInfo();

View File

@@ -52,7 +52,7 @@ public class PayTrdV10Service implements PayTrdService {
params.put("pay_orderid", payOrderInfoDTO.getOrderNo());
params.put("pay_applydate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("pay_bankcode", payTrdConfig.getProductId(wx));
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("pay_notifyurl", notifyUrl);
params.put("pay_amount", payOrderInfoDTO.getPriceYuanStr());
MultiValueMap<String, String> map = PayMd5Util.createParamsOfMap(params, payTrdConfig.getSign(), "pay_md5sign");

View File

@@ -52,7 +52,7 @@ public class PayTrdV11Service implements PayTrdService {
params.put("pay_orderid", payOrderInfoDTO.getOrderNo());
params.put("pay_applydate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("pay_bankcode", payTrdConfig.getProductId(wx));
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("pay_notifyurl", notifyUrl);
params.put("pay_amount", payOrderInfoDTO.getPriceYuanStr());
MultiValueMap<String, String> map = PayMd5Util.createParamsOfMap(params, payTrdConfig.getSign(), "pay_md5sign");

View File

@@ -107,7 +107,7 @@ public class PayTrdV12Service implements PayTrdService {
}else {
bizRequest.setFuncCodeList(Collections.singletonList(FuncCodeEnum.FC02020004.getCode()));
}
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
payerInfo.setFrontUrl(notifyUrl);
payerInfo.setPayExtra(payExtraList);
bizRequest.setPayerInfo(payerInfo);

View File

@@ -51,7 +51,7 @@ public class PayTrdV13Service implements PayTrdService {
params.put("type", "alipay");
}
params.put("out_trade_no", payOrderInfoDTO.getOrderNo());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notify_url", notifyUrl);
// params.put("return_url", notifyUrl);
params.put("name", payOrderInfoDTO.getSubject());

View File

@@ -3,6 +3,7 @@ package com.ruoyi.cai.trdpay.handle;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cai.domain.PayTrdConfig;
import com.ruoyi.cai.pay.PayManager;
import com.ruoyi.cai.pay.PayOrderInfoDTO;
import com.ruoyi.cai.pay.PayReturnResp;
import com.ruoyi.cai.trdpay.PayTrdService;
@@ -23,6 +24,8 @@ import java.util.Map;
@Slf4j
public class PayTrdV14Service implements PayTrdService {
@Autowired
private PayManager payManager;
@Autowired
private V14Manager v14Manager;
@@ -39,7 +42,9 @@ public class PayTrdV14Service implements PayTrdService {
v14PayResp.setPrice(payOrderInfoDTO.getPrice());
v14PayResp.setSubject(payOrderInfoDTO.getSubject());
v14PayResp.setOrderNo(payOrderInfoDTO.getOrderNo());
v14PayResp.setApi("apidjwklqw.mvsdiv.cn");
String notifyDomain = payManager.getNotifyDomain(payTrdConfig.getNotifyUrl());
String api = notifyDomain.replace("https://", "").replace("http://", "");
v14PayResp.setApi(api);
return PayReturnResp.createEfps(v14PayResp);
}

View File

@@ -53,7 +53,7 @@ public class PayTrdV15Service implements PayTrdService {
}else{
params.put("pay_bankcode", payTrdConfig.getAliProductId());
}
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("pay_notifyurl", notifyUrl);
params.put("pay_callbackurl", notifyUrl);
params.put("pay_amount", payOrderInfoDTO.getPriceYuanStr());

View File

@@ -48,7 +48,7 @@ public class PayTrdV1Service implements PayTrdService {
params.put("productId", payTrdConfig.getProductId(wx));
params.put("mchOrderNo", payOrderInfoDTO.getOrderNo());
params.put("amount", payOrderInfoDTO.getPriceFenStr());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notifyUrl", notifyUrl);
String para = PayMd5Util.createParams(params, payTrdConfig.getSign());
String gatewayUrl = getGatewayUrl(payTrdConfig);

View File

@@ -57,7 +57,7 @@ public class PayTrdV2Service implements PayTrdService {
params.put("subject",payOrderInfoDTO.getSubject());
params.put("body",payOrderInfoDTO.getBody());
params.put("currency","cny");
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notifyUrl", notifyUrl);
String para = PayMd5Util.createParams(params, payTrdConfig.getSign());
String gatewayUrl = getGatewayUrl(payTrdConfig);

View File

@@ -46,7 +46,7 @@ public class PayTrdV3Service implements PayTrdService {
params.put("productId", payTrdConfig.getProductId(wx));
params.put("mchOrderNo", payOrderInfoDTO.getOrderNo());
params.put("amount", payOrderInfoDTO.getPriceFenStr());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notifyUrl", notifyUrl);
String para = PayMd5Util.createParams(params, payTrdConfig.getSign());
String gatewayUrl = getGatewayUrl(payTrdConfig);

View File

@@ -45,7 +45,7 @@ public class PayTrdV4Service implements PayTrdService {
params.put("tradeType", payTrdConfig.getProductId(wx));
params.put("merchantPayNo", payOrderInfoDTO.getOrderNo());
params.put("amt", payOrderInfoDTO.getPrice().toString());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notifyUrl", notifyUrl);
params.put("goodsName",payOrderInfoDTO.getBody());
String para = PayMd5Util.createParams(params, payTrdConfig.getSign());

View File

@@ -54,7 +54,7 @@ public class PayTrdV5Service implements PayTrdService {
params.put("pay_orderid", payOrderInfoDTO.getOrderNo());
params.put("pay_applydate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("pay_bankcode", payTrdConfig.getProductId(wx));
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("pay_notifyurl", notifyUrl);
params.put("pay_amount", payOrderInfoDTO.getPriceYuanStr());
MultiValueMap<String, String> map = PayMd5Util.createParamsOfMap(params, payTrdConfig.getSign(), "pay_md5sign");

View File

@@ -50,7 +50,7 @@ public class PayTrdV6Service implements PayTrdService {
params.put("productId", payTrdConfig.getProductId(wx));
params.put("extra", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("amount", payOrderInfoDTO.getPriceFenStr());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notifyUrl", notifyUrl);
params.put("subject",payOrderInfoDTO.getSubject());
params.put("body",payOrderInfoDTO.getBody());

View File

@@ -52,7 +52,7 @@ public class PayTrdV7Service implements PayTrdService {
params.put("channelType", payTrdConfig.getProductId(wx));
params.put("merchantOrderNo", payOrderInfoDTO.getOrderNo());
params.put("amount", payOrderInfoDTO.getPriceYuanStr());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notifyUrl", notifyUrl);
params.put("ip", ServletUtils.getClientIP());
params.put("title", payOrderInfoDTO.getSubject());

View File

@@ -73,7 +73,7 @@ public class PayTrdV8Service implements PayTrdService {
params.put("device", "mobile");
params.put("type", "alipay");
params.put("out_trade_no", payOrderInfoDTO.getOrderNo());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("notify_url", notifyUrl);
params.put("name", payOrderInfoDTO.getSubject());
params.put("money", payOrderInfoDTO.getPriceYuanStr());

View File

@@ -48,7 +48,7 @@ public class PayTrdV9Service implements PayTrdService {
params.put("pay_orderid", payOrderInfoDTO.getOrderNo());
params.put("pay_applydate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("pay_bankcode", payTrdConfig.getProductId(wx));
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
String notifyUrl = payManager.getNotifyUrl(payTrdConfig, type, wx);
params.put("pay_notifyurl", notifyUrl);
params.put("pay_amount", payOrderInfoDTO.getPriceYuanStr());
MultiValueMap<String, String> map = PayMd5Util.createParamsOfMap(params, payTrdConfig.getSign(), "pay_md5sign");

View File

@@ -0,0 +1,157 @@
package com.ruoyi.cai.util;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* 密码安全校验工具类
*
* @author 77
* @date 2025-01-15
*/
public class PasswordUtil {
/**
* 密码最小长度
*/
private static final int MIN_PASSWORD_LENGTH = 8;
/**
* 常见弱密码列表
*/
private static final Set<String> COMMON_WEAK_PASSWORDS = new HashSet<>(Arrays.asList(
"123456",
"12345678",
"123456789",
"password",
"qwerty",
"12345",
"1234567",
"1234567890",
"111111",
"000000",
"888888",
"666666",
"A123456",
"a123456",
"123456a",
"123456A",
"123456ab",
"123456AB",
"Password123",
"password123",
"Qwerty123",
"qwerty123",
"Admin123",
"admin123",
"Abc123456",
"abc123456"
));
/**
* 字母正则表达式(包含大小写)
*/
private static final Pattern LETTER_PATTERN = Pattern.compile("[a-zA-Z]");
/**
* 数字正则表达式
*/
private static final Pattern DIGIT_PATTERN = Pattern.compile("[0-9]");
/**
* 校验密码安全性
*
* @param password 用户输入的密码
* @return 校验结果对象
*/
public static PasswordValidationResult validatePassword(String password) {
PasswordValidationResult result = new PasswordValidationResult();
// 1. 检查是否为空
if (password == null || password.isEmpty()) {
result.setValid(false);
result.setErrorMessage("密码不能为空");
return result;
}
// 2. 检查长度
if (password.length() < MIN_PASSWORD_LENGTH) {
result.setValid(false);
result.setErrorMessage("密码长度不能少于" + MIN_PASSWORD_LENGTH + "");
return result;
}
// 3. 检查是否包含字母
if (!LETTER_PATTERN.matcher(password).find()) {
result.setValid(false);
result.setErrorMessage("密码必须包含字母");
return result;
}
// 4. 检查是否包含数字
if (!DIGIT_PATTERN.matcher(password).find()) {
result.setValid(false);
result.setErrorMessage("密码必须包含数字");
return result;
}
// 5. 检查是否为常见弱密码
String lowerCasePassword = password.toLowerCase();
for (String weakPassword : COMMON_WEAK_PASSWORDS) {
if (lowerCasePassword.equals(weakPassword.toLowerCase())) {
result.setValid(false);
result.setErrorMessage("密码过于简单,请使用更复杂的密码");
return result;
}
}
// 校验通过
result.setValid(true);
result.setErrorMessage(null);
return result;
}
/**
* 快速校验密码仅返回boolean不提供详细错误信息
*
* @param password 用户输入的密码
* @return true-密码安全false-密码不安全
*/
public static boolean isPasswordValid(String password) {
return validatePassword(password).isValid();
}
/**
* 密码校验结果内部类
*/
public static class PasswordValidationResult {
/**
* 是否校验通过
*/
private boolean valid;
/**
* 错误信息校验失败时有值校验成功时为null
*/
private String errorMessage;
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
}

View File

@@ -12,4 +12,5 @@ public class RedisConstant {
public static final String INIT_ROOM_LOCK = REDIS_P + "lock:initRoom:%s-%s";
public static final String Y4X_REDIS_CACHE = REDIS_P + "shareUrl:y4x:%s";
public static final String START_SEND_MESSAGE_CACHE = REDIS_P + "starSendMessage:%s";
public static final String POINT_DAYS_USER = REDIS_P + "pointDaysUser:%s:%s";
}

View File

@@ -4,22 +4,5 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.cai.mapper.PrizeInfoMapper">
<resultMap type="com.ruoyi.cai.domain.PrizeInfo" id="PrizeInfoResult">
<result property="id" column="id"/>
<result property="prizeName" column="prize_name"/>
<result property="prizeDesc" column="prize_desc"/>
<result property="prizeImg" column="prize_img"/>
<result property="winProbability" column="win_probability"/>
<result property="guaranteeDraws" column="guarantee_draws"/>
<result property="minWinDraws" column="min_win_draws"/>
<result property="stock" column="stock"/>
<result property="prizeType" column="prize_type"/>
<result property="prizePrice" column="prize_price"/>
<result property="autoGive" column="auto_give"/>
<result property="isThank" column="is_thank"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
</mapper>

View File

@@ -4,24 +4,5 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.cai.mapper.PrizeOnlineMapper">
<resultMap type="com.ruoyi.cai.domain.PrizeOnline" id="PrizeOnlineResult">
<result property="id" column="id"/>
<result property="prizeId" column="prize_id"/>
<result property="gender" column="gender"/>
<result property="prizeName" column="prize_name"/>
<result property="prizeDesc" column="prize_desc"/>
<result property="prizeImg" column="prize_img"/>
<result property="winProbability" column="win_probability"/>
<result property="guaranteeDraws" column="guarantee_draws"/>
<result property="minWinDraws" column="min_win_draws"/>
<result property="stock" column="stock"/>
<result property="prizeType" column="prize_type"/>
<result property="prizePrice" column="prize_price"/>
<result property="autoGive" column="auto_give"/>
<result property="isThank" column="is_thank"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
</mapper>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.cai.mapper.PrizeWinningRecordMapper">
<select id="pageAdmin" resultType="com.ruoyi.cai.dto.admin.vo.winningRecord.PrizeWinningRecordAdminVO">
select t2.usercode, t2.mobile, t2.nickname, t1.*
from cai_prize_winning_record t1
left join cai_user t2 on t1.user_id = t2.id
<where>
<if test="query.usercode != null and query.usercode != ''">
and t2.usercode = #{query.usercode}
</if>
<if test="query.mobile != null and query.mobile != ''">
and t2.mobile = #{query.mobile}
</if>
<if test="query.prizeName != null and query.prizeName != ''">
and t1.prize_name like concat('%',#{query.prizeName},'%')
</if>
<if test="query.autoGive != null">
and t1.auto_give = #{query.autoGive}
</if>
<if test="query.giveStatus != null">
and t1.give_status = #{query.giveStatus}
</if>
</where>
order by t1.create_time desc
</select>
</mapper>

View File

@@ -25,6 +25,8 @@ public interface ISysOssService {
SysOssVo getById(Long ossId);
SysOssVo upload(MultipartFile file, String configKey);
SysOssVo upload(MultipartFile file);
SysOssVo upload(File file);

View File

@@ -133,6 +133,22 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
}
}
@Override
public SysOssVo upload(MultipartFile file, String configKey) {
String originalfileName = file.getOriginalFilename();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
OssClient storage = OssFactory.instance(configKey);
UploadResult uploadResult;
try {
uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
} catch (IOException e) {
throw new ServiceException(e.getMessage());
}
// 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
}
@Override
public SysOssVo upload(MultipartFile file) {
String originalfileName = file.getOriginalFilename();