diff --git a/src/main/java/com/sqx/common/aspect/DebounceAspect.java b/src/main/java/com/sqx/common/aspect/DebounceAspect.java index 992f3471..b88c8c42 100644 --- a/src/main/java/com/sqx/common/aspect/DebounceAspect.java +++ b/src/main/java/com/sqx/common/aspect/DebounceAspect.java @@ -1,6 +1,7 @@ package com.sqx.common.aspect; import com.sqx.common.annotation.Debounce; +import com.sqx.common.exception.SqxException; import com.sqx.common.utils.Result; import com.sqx.common.utils.SpelUtil; import org.apache.commons.lang3.StringUtils; @@ -26,8 +27,8 @@ import java.util.concurrent.locks.ReentrantLock; public class DebounceAspect { @Pointcut("@annotation(com.sqx.common.annotation.Debounce)") - public void logPointCut() { - + public void logPointCut() { + } // 用于存储基于方法和入参情况的上次执行时间,结构为:方法签名 -> (入参值 -> 上次执行时间) @@ -87,7 +88,16 @@ public class DebounceAspect { if (lastTime == null || currentTime - timeUnit.toMillis(interval) >= lastTime) { // 满足防抖间隔,更新上次执行时间,并执行目标方法 methodExecutionTimeMap.put(targetValue, currentTime); - return joinPoint.proceed(); + try { + return joinPoint.proceed(); + }catch (Exception e) { + if (e instanceof SqxException && ((SqxException) e).getCode() == 407) { + methodExecutionTimeMap.remove(targetValue); + } + throw e; + } + + } // 在防抖间隔内,不执行目标方法,直接返回 return Result.error("请求频繁,请重试"); diff --git a/src/main/java/com/sqx/common/utils/SqlFetcher.java b/src/main/java/com/sqx/common/utils/SqlFetcher.java new file mode 100644 index 00000000..f6973b47 --- /dev/null +++ b/src/main/java/com/sqx/common/utils/SqlFetcher.java @@ -0,0 +1,35 @@ +package com.sqx.common.utils; + +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ParameterMap; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author tankaikai + * @since 2025-03-28 10:16 + */ +@Component +public class SqlFetcher { + @Autowired + private SqlSessionFactory sqlSessionFactory; + + public String getSqlFromXml(String statementId, Object parameterObject) { + // 获取Configuration对象 + Configuration configuration = sqlSessionFactory.getConfiguration(); + try { + // 获取指定ID的MappedStatement对象 + MappedStatement mappedStatement = configuration.getMappedStatement(statementId); + // 获取SQL语句 + if(parameterObject instanceof String){ + SqlUtil.escapeOrderBySql((String) parameterObject); + } + return mappedStatement.getBoundSql(parameterObject).getSql(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/com/sqx/common/utils/SqlUtil.java b/src/main/java/com/sqx/common/utils/SqlUtil.java new file mode 100644 index 00000000..66b79a34 --- /dev/null +++ b/src/main/java/com/sqx/common/utils/SqlUtil.java @@ -0,0 +1,66 @@ +package com.sqx.common.utils; + +import cn.hutool.core.util.StrUtil; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil { + /** + * SQL语法检查正则:符合两个关键字(有先后顺序)才算匹配 + */ + private static final Pattern SQL_SYNTAX_PATTERN = Pattern.compile("(insert|delete|update|select|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)" + + "\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)|(select\\s*\\*\\s*from\\s+)" + + "|if\\s*\\(.*\\)|select\\s*\\(.*\\)|substr\\s*\\(.*\\)|substring\\s*\\(.*\\)|char\\s*\\(.*\\)|concat\\s*\\(.*\\)|benchmark\\s*\\(.*\\)|sleep\\s*\\(.*\\)|(and|or)\\s+.*", Pattern.CASE_INSENSITIVE); + /** + * 使用'、;或注释截断SQL检查正则 + */ + private static final Pattern SQL_COMMENT_PATTERN = Pattern.compile("'.*(or|union|--|#|/\\*|;)", Pattern.CASE_INSENSITIVE); + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + if (StrUtil.isNotEmpty(value)) { + if (check(value)) { + throw new IllegalArgumentException("参数不符合规范,不能进行查询"); + } + if (value.length() > ORDER_BY_MAX_LENGTH) { + throw new IllegalArgumentException("参数已超过最大限制,不能进行查询"); + } + } + return value; + } + + /** + * 检查参数是否存在 SQL 注入 + * + * @param value 检查参数 + * @return true 非法 false 合法 + */ + public static boolean check(String value) { + Objects.requireNonNull(value); + // 处理是否包含SQL注释字符 || 检查是否包含SQL注入敏感字符 + return SQL_COMMENT_PATTERN.matcher(value).find() || SQL_SYNTAX_PATTERN.matcher(value).find(); + } + + /** + * 刪除字段转义符单引号双引号 + * + * @param text 待处理字段 + */ + public static String removeEscapeCharacter(String text) { + Objects.nonNull(text); + return text.replaceAll("\"", "").replaceAll("'", ""); + } +} diff --git a/src/main/java/com/sqx/modules/app/controller/app/AppLoginController.java b/src/main/java/com/sqx/modules/app/controller/app/AppLoginController.java index 4e3ed94a..3a702059 100644 --- a/src/main/java/com/sqx/modules/app/controller/app/AppLoginController.java +++ b/src/main/java/com/sqx/modules/app/controller/app/AppLoginController.java @@ -1,6 +1,7 @@ package com.sqx.modules.app.controller.app; +import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.sqx.common.annotation.Debounce; @@ -9,7 +10,6 @@ import com.sqx.modules.app.annotation.Login; import com.sqx.modules.app.dto.BindWxDTO; import com.sqx.modules.app.dto.LoginDTO; import com.sqx.modules.app.dto.RegisterDTO; -import com.sqx.modules.app.dto.UserInviteDTO; import com.sqx.modules.app.entity.UserEntity; import com.sqx.modules.app.service.IAppleService; import com.sqx.modules.app.service.UserService; @@ -29,6 +29,8 @@ import org.springframework.web.bind.annotation.*; import weixin.popular.api.SnsAPI; import weixin.popular.bean.sns.SnsToken; +import java.util.HashMap; + /** * APP登录授权 * @@ -174,10 +176,22 @@ public class AppLoginController { @PostMapping("/bindWx") @ResponseBody @Debounce(interval = 2500, value = "#code") - public Result bindWx(@RequestBody BindWxDTO bindWxDTO, @RequestHeader Long userId) { + public Result bindWx(@RequestBody BindWxDTO bindWxDTO, @RequestAttribute("userId") Long userId) { return userService.bindWx(bindWxDTO, userId); } + @Login + @GetMapping("/bindStatus") + @ResponseBody + public Result bindWx(@RequestAttribute("userId") Long userId) { + UserEntity userEntity = userService.queryByUserId(userId); + return Result.success(new HashMap(){{ + put("data", new HashMap(){{ + put("wxBind", StrUtil.isNotBlank(userEntity.getWxOpenId())); + }}); + }}); + } + @PostMapping("/bindWxOpenPhone") @ApiOperation("微信公众号绑定手机号") public Result bindWxOpenPhone(Long userId,String phone,String msg){ diff --git a/src/main/java/com/sqx/modules/app/service/impl/UserServiceImpl.java b/src/main/java/com/sqx/modules/app/service/impl/UserServiceImpl.java index dd1b5191..a5130233 100644 --- a/src/main/java/com/sqx/modules/app/service/impl/UserServiceImpl.java +++ b/src/main/java/com/sqx/modules/app/service/impl/UserServiceImpl.java @@ -1829,17 +1829,20 @@ public class UserServiceImpl extends ServiceImpl implements @Override public Result bindWx(BindWxDTO bindWxDTO, Long userId) { //微信appid - CommonInfo one = commonInfoService.findOne(5); + CommonInfo one = commonInfoService.findOne(937); //微信秘钥 - CommonInfo two = commonInfoService.findOne(21); + CommonInfo two = commonInfoService.findOne(936); SnsToken snsToken = SnsAPI.oauth2AccessToken(one.getValue(), two.getValue(), bindWxDTO.getCode()); + if (!snsToken.isSuccess()) { + return Result.error("获取失败"); + } String openid = snsToken.getOpenid(); int count = count(new LambdaQueryWrapper().eq(UserEntity::getWxOpenId, openid)); if (count > 0) { return Result.error("该微信已被其他用户绑定"); } - UserEntity userEntity = getOne(new LambdaQueryWrapper().eq(UserEntity::getUserId, userId)); + UserEntity userEntity = queryByUserId(userId); if (StringUtils.isNotBlank(userEntity.getWxOpenId())) { return Result.error("当前用户已绑定微信"); } diff --git a/src/main/java/com/sqx/modules/ext/controller/AppExtSysController.java b/src/main/java/com/sqx/modules/ext/controller/AppExtSysController.java new file mode 100644 index 00000000..a7371e9d --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/controller/AppExtSysController.java @@ -0,0 +1,31 @@ +package com.sqx.modules.ext.controller; + +import cn.hutool.core.exceptions.ValidateException; +import com.sqx.common.utils.Result; +import com.sqx.modules.ext.param.InviteFriendConfigParam; +import com.sqx.modules.ext.service.ExtSysService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 扩展功能控制层 + * @author tankaikai + * @since 2025-03-28 09:32 + */ +@RestController +@Api(value = "APP扩展功能", tags = {"APP扩展功能"}) +@RequestMapping("/app/ext/sys") +public class AppExtSysController { + + @Autowired + private ExtSysService extSysService; + + @GetMapping("/invite/friend/config/get") + @ApiOperation("获取邀请好友配置信息") + public Result getInviteFriendConfig(){ + InviteFriendConfigParam data = extSysService.getInviteFriendConfig(); + return Result.success().put("data", data); + } +} diff --git a/src/main/java/com/sqx/modules/ext/controller/ExtSysController.java b/src/main/java/com/sqx/modules/ext/controller/ExtSysController.java new file mode 100644 index 00000000..20628501 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/controller/ExtSysController.java @@ -0,0 +1,88 @@ +package com.sqx.modules.ext.controller; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.exceptions.ValidateException; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.sqx.common.utils.PageUtils; +import com.sqx.common.utils.Result; +import com.sqx.modules.app.entity.App; +import com.sqx.modules.app.entity.VipDetails; +import com.sqx.modules.coupon.entity.Coupon; +import com.sqx.modules.ext.param.InviteFriendConfigParam; +import com.sqx.modules.ext.service.ExtSysService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.ws.rs.GET; +import java.util.List; + +/** + * 扩展功能控制层 + * + * @author tankaikai + * @since 2025-03-28 09:32 + */ +@RestController +@Api(value = "ADMIN扩展功能", tags = {"ADMIN扩展功能"}) +@RequestMapping("/ext/sys") +public class ExtSysController { + + @Autowired + private ExtSysService extSysService; + + @PostMapping("/invite/friend/config/save") + @ApiOperation("保存邀请好友配置信息") + public Result saveInviteFriendConfig(@RequestBody InviteFriendConfigParam param) { + try { + extSysService.saveInviteFriendConfig(param); + } catch (ValidateException e) { + return Result.error(e.getMessage()); + } + return Result.success(); + } + + @GetMapping("/invite/friend/config/get") + @ApiOperation("获取邀请好友配置信息") + public Result getInviteFriendConfig() { + InviteFriendConfigParam data = extSysService.getInviteFriendConfig(); + return Result.success().put("data", data); + } + + @GetMapping("/invite/friend/award/page") + @ApiOperation("邀请好友奖励分页") + public Result getInviteFriendAwardPage(Integer page, Integer limit, String keywords) { + PageUtils data = extSysService.queryInviteFriendRecord(page, limit, keywords); + return Result.success().put("data", data); + } + + @GetMapping("/invite/friend/signIn/page") + @ApiOperation("邀请好友奖励-签到人数分页") + public Result getInviteFriendSignInPage(Integer page, Integer limit, String userId) { + PageUtils data = extSysService.queryInviteSignInRecord(page, limit, Convert.toLong(userId)); + return Result.success().put("data", data); + } + + @GetMapping("/invite/friend/award/detail/page") + @ApiOperation("邀请好友奖励详情分页") + public Result getInviteFriendAwardDetailPage(Integer page, Integer limit, String userId) { + PageUtils data = extSysService.queryInviteAwardDetailRecord(page, limit, Convert.toLong(userId)); + return Result.success().put("data", data); + } + + @GetMapping("/lottery/count/query/page") + @ApiOperation("抽奖次数查询-分页") + public Result getLotteryCountQueryPage(Integer page, Integer limit, String keywords) { + PageUtils data = extSysService.queryLotteryCountPage(page, limit, keywords); + return Result.success().put("data", data); + } + + @GetMapping("/lottery/detail/page") + @ApiOperation("抽奖详情查询-分页") + public Result getLotteryDetailPage(Integer page, Integer limit, String userId) { + PageUtils data = extSysService.queryLotteryDetailPage(page, limit, Convert.toLong(userId)); + return Result.success().put("data", data); + } +} diff --git a/src/main/java/com/sqx/modules/ext/dao/ExtSysDao.java b/src/main/java/com/sqx/modules/ext/dao/ExtSysDao.java new file mode 100644 index 00000000..2750ec90 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/dao/ExtSysDao.java @@ -0,0 +1,28 @@ +package com.sqx.modules.ext.dao; + +import com.sqx.modules.ext.dto.*; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 扩展系统数据访问层 + * + * @author tankaikai + * @since 2025-03-28 10:39 + */ +@Mapper +public interface ExtSysDao { + List findInviteFriendList(@Param("keywords") String keywords); + + List findInviteSignInList(@Param("userId") Long userId); + + List findInviteAwardDetailList(@Param("userId") Long userId); + + List findLotteryCountList(@Param("keywords") String keywords); + + List findLotteryDetailPage(@Param("userId") Long userId); + + +} diff --git a/src/main/java/com/sqx/modules/ext/dto/InviteAwardDTO.java b/src/main/java/com/sqx/modules/ext/dto/InviteAwardDTO.java new file mode 100644 index 00000000..cdecfa6f --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/dto/InviteAwardDTO.java @@ -0,0 +1,41 @@ +package com.sqx.modules.ext.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 邀请奖励明细 + * + * @author tankaikai + * @since 2025-03-28 10:07 + */ +@Data +public class InviteAwardDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 奖励id + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + /** + * 发放金额 + */ + private BigDecimal amount; + /** + * 有效邀请人 + */ + private String userPhone; + /** + * 发放时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} diff --git a/src/main/java/com/sqx/modules/ext/dto/InviteFriendDTO.java b/src/main/java/com/sqx/modules/ext/dto/InviteFriendDTO.java new file mode 100644 index 00000000..5b9ae6e0 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/dto/InviteFriendDTO.java @@ -0,0 +1,50 @@ +package com.sqx.modules.ext.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 邀请好友DTO + * + * @author tankaikai + * @since 2025-03-28 09:56 + */ +@Data +public class InviteFriendDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + + /** + * 用户名称 + */ + private String userName; + + /** + * 手机号 + */ + private String phone; + + /** + * 头像地址 + */ + private String avatar; + + /** + * 签到人数 + */ + private Integer signInNum; + /** + * 已发放奖励金额 + */ + private BigDecimal awardAmount; +} diff --git a/src/main/java/com/sqx/modules/ext/dto/LotteryCountQueryDTO.java b/src/main/java/com/sqx/modules/ext/dto/LotteryCountQueryDTO.java new file mode 100644 index 00000000..706c1ae0 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/dto/LotteryCountQueryDTO.java @@ -0,0 +1,57 @@ +package com.sqx.modules.ext.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +/** + * 抽奖次数查询DTO + * + * @author tankaikai + * @since 2025-03-28 10:24 + */ +@Data +public class LotteryCountQueryDTO implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 账号ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + /** + * 头像地址 + */ + private String avatar; + /** + * 用户名称 + */ + private String userName; + /** + * 手机号 + */ + private String phone; + /** + * 共解锁 + */ + private Integer unlocked; + /** + * 今日解锁 + */ + private Integer todayUnlocked; + /** + * 共获得抽奖次数 + */ + private Integer totalDrawCount; + /** + * 剩余抽奖次数 + */ + private Integer residueDrawCount; + /** + * 今日抽奖次数 + */ + private Integer todayDrawCount; + +} diff --git a/src/main/java/com/sqx/modules/ext/dto/LotteryDetailDTO.java b/src/main/java/com/sqx/modules/ext/dto/LotteryDetailDTO.java new file mode 100644 index 00000000..35cc9784 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/dto/LotteryDetailDTO.java @@ -0,0 +1,39 @@ +package com.sqx.modules.ext.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 抽奖详情DTO类 + * + * @author tankaikai + * @since 2025-03-28 10:24 + */ +@Data +public class LotteryDetailDTO implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 抽奖id + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + /** + * 用户名称 + */ + private String name; + /** + * 手机号 + */ + private String number; + /** + * 发放时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} diff --git a/src/main/java/com/sqx/modules/ext/dto/SignInNumDTO.java b/src/main/java/com/sqx/modules/ext/dto/SignInNumDTO.java new file mode 100644 index 00000000..09829155 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/dto/SignInNumDTO.java @@ -0,0 +1,72 @@ +package com.sqx.modules.ext.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 签到人数DTO + * + * @author tankaikai + * @since 2025-03-28 10:24 + */ +@Data +public class SignInNumDTO implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 签到账号ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + /** + * 用户名称 + */ + private String userName; + + /** + * 手机号 + */ + private String phone; + + /** + * 真实姓名 + */ + private String realName; + /** + * 身份证号 + */ + private String idCardNo; + /** + * 银行名称 + */ + private String bankName; + /** + * 银行卡号 + */ + private String bankCardNo; + /** + * 银行预留手机号 + */ + private String mobile; + /** + * 省 + */ + private String province; + /** + * 市 + */ + private String city; + /** + * 开户行 + */ + private String bankBranch; + /** + * 发放时间 + */ + private String createTime; +} diff --git a/src/main/java/com/sqx/modules/ext/param/InviteFriendConfigParam.java b/src/main/java/com/sqx/modules/ext/param/InviteFriendConfigParam.java new file mode 100644 index 00000000..ce54d68f --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/param/InviteFriendConfigParam.java @@ -0,0 +1,22 @@ +package com.sqx.modules.ext.param; + +import lombok.Data; + +/** + * 邀请好友配置入参 + * @author tankaikai + * @since 2025-03-28 09:32 + */ +@Data +public class InviteFriendConfigParam { + + /** + * 邀请好友海报图片地址 + */ + private String imageUrl; + + /** + * 邀请文案 + */ + private String tips; +} diff --git a/src/main/java/com/sqx/modules/ext/service/ExtSysService.java b/src/main/java/com/sqx/modules/ext/service/ExtSysService.java new file mode 100644 index 00000000..be082b1e --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/service/ExtSysService.java @@ -0,0 +1,26 @@ +package com.sqx.modules.ext.service; + +import com.sqx.common.utils.PageUtils; +import com.sqx.modules.ext.param.InviteFriendConfigParam; + +/** + * 扩展系统服务接口 + * @author tankaikai + * @since 2025-03-28 09:32 + */ +public interface ExtSysService { + + void saveInviteFriendConfig(InviteFriendConfigParam param); + + InviteFriendConfigParam getInviteFriendConfig(); + + PageUtils queryInviteFriendRecord(Integer page, Integer limit, String keywords); + + PageUtils queryInviteSignInRecord(Integer page, Integer limit, Long userId); + + PageUtils queryInviteAwardDetailRecord(Integer page, Integer limit, Long userId); + + PageUtils queryLotteryCountPage(Integer page, Integer limit, String keywords); + + PageUtils queryLotteryDetailPage(Integer page, Integer limit, Long userId); +} diff --git a/src/main/java/com/sqx/modules/ext/service/impl/ExtSysServiceImpl.java b/src/main/java/com/sqx/modules/ext/service/impl/ExtSysServiceImpl.java new file mode 100644 index 00000000..bffdd399 --- /dev/null +++ b/src/main/java/com/sqx/modules/ext/service/impl/ExtSysServiceImpl.java @@ -0,0 +1,142 @@ +package com.sqx.modules.ext.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.exceptions.ValidateException; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.db.Db; +import com.sqx.common.utils.PageUtils; +import com.sqx.common.utils.SqlFetcher; +import com.sqx.modules.common.dao.CommonInfoDao; +import com.sqx.modules.common.entity.CommonInfo; +import com.sqx.modules.ext.dao.ExtSysDao; +import com.sqx.modules.ext.dto.*; +import com.sqx.modules.ext.param.InviteFriendConfigParam; +import com.sqx.modules.ext.service.ExtSysService; +import lombok.SneakyThrows; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 扩展功能服务实现类 + * + * @author tankaikai + * @since 2025-03-28 09:32 + */ +@Service +public class ExtSysServiceImpl implements ExtSysService { + @Autowired + private CommonInfoDao commonInfoDao; + @Autowired + private ExtSysDao extSysDao; + @Autowired + private SqlFetcher sqlFetcher; + @Value("${spring.profiles.active}") + private String profiles; + + @Override + public void saveInviteFriendConfig(InviteFriendConfigParam param) { + if (StrUtil.isBlank(param.getImageUrl())) { + throw new ValidateException("顶部图片地址不能为空"); + } + if (StrUtil.isBlank(param.getTips())) { + throw new ValidateException("邀请文案不能为空"); + } + if (param.getTips().length() > 20) { + throw new ValidateException("邀请文案不能大于20个字符"); + } + CommonInfo commonInfo = commonInfoDao.selectById(999L); + if (commonInfo == null) { + commonInfo = new CommonInfo(); + commonInfo.setId(999L); + commonInfo.setCreateAt(DateUtil.now()); + commonInfo.setMax(param.getTips()); + commonInfo.setMin("邀请好友配置"); + commonInfo.setValue(param.getImageUrl()); + commonInfo.setType(Convert.toInt(commonInfo.getId())); + commonInfo.setIsAppUse(1); + commonInfoDao.insert(commonInfo); + return; + } + commonInfo.setMax(param.getTips()); + commonInfo.setValue(param.getImageUrl()); + commonInfoDao.updateById(commonInfo); + } + + @Override + public InviteFriendConfigParam getInviteFriendConfig() { + CommonInfo commonInfo = commonInfoDao.selectById(999L); + if (commonInfo == null) { + return null; + } + InviteFriendConfigParam data = new InviteFriendConfigParam(); + data.setTips(commonInfo.getMax()); + data.setImageUrl(commonInfo.getValue()); + return data; + } + + @Override + public PageUtils queryInviteFriendRecord(Integer page, Integer limit, String keywords) { + String sql = sqlFetcher.getSqlFromXml("com.sqx.modules.ext.dao.ExtSysDao.findInviteFriendList", keywords); + int totalCount = getTotalCount(sql); + List list = getQueryList(sql, InviteFriendDTO.class, totalCount, page, limit); + return new PageUtils(list, totalCount, limit, page); + } + + @Override + public PageUtils queryInviteSignInRecord(Integer page, Integer limit, Long userId) { + String sql = sqlFetcher.getSqlFromXml("com.sqx.modules.ext.dao.ExtSysDao.findInviteSignInList", userId); + int totalCount = getTotalCount(sql); + List list = getQueryList(sql, SignInNumDTO.class, totalCount, page, limit); + return new PageUtils(list, totalCount, limit, page); + } + + @Override + public PageUtils queryInviteAwardDetailRecord(Integer page, Integer limit, Long userId) { + String sql = sqlFetcher.getSqlFromXml("com.sqx.modules.ext.dao.ExtSysDao.findInviteAwardDetailList", userId); + int totalCount = getTotalCount(sql); + List list = getQueryList(sql, InviteAwardDTO.class, totalCount, page, limit); + return new PageUtils(list, totalCount, limit, page); + } + + @Override + public PageUtils queryLotteryCountPage(Integer page, Integer limit, String keywords) { + String sql = sqlFetcher.getSqlFromXml("com.sqx.modules.ext.dao.ExtSysDao.findLotteryCountList", keywords); + int totalCount = getTotalCount(sql); + List list = getQueryList(sql, LotteryCountQueryDTO.class, totalCount, page, limit); + return new PageUtils(list, totalCount, limit, page); + } + + @Override + public PageUtils queryLotteryDetailPage(Integer page, Integer limit, Long userId) { + String sql = sqlFetcher.getSqlFromXml("com.sqx.modules.ext.dao.ExtSysDao.findLotteryDetailPage", userId); + int totalCount = getTotalCount(sql); + List list = getQueryList(sql, LotteryDetailDTO.class, totalCount, page, limit); + return new PageUtils(list, totalCount, limit, page); + } + + @SneakyThrows + private int getTotalCount(String sql) { + String countSql = StrUtil.format("select count(*) from ({}) as tmp", sql); + System.out.println("CountSQL:" + countSql); + Number count = Db.use(profiles).queryNumber(countSql); + return count.intValue(); + } + + @SneakyThrows + private List getQueryList(String sql, Class beanClass, int totalCount, Integer page, Integer limit) { + if(totalCount == 0){ + return new ArrayList<>(); + } + page = ObjectUtil.defaultIfNull(page, 1); + limit = ObjectUtil.defaultIfNull(limit, 10); + String querySql = StrUtil.format("select * from ({}) as tmp limit {}, {}", sql, (page - 1) * limit, limit); + System.out.println("QuerySql:" + querySql); + return Db.use(profiles).query(querySql, beanClass); + } +} diff --git a/src/main/java/com/sqx/modules/orders/controller/app/AppOrdersController.java b/src/main/java/com/sqx/modules/orders/controller/app/AppOrdersController.java index 74d598a6..efe6fccd 100644 --- a/src/main/java/com/sqx/modules/orders/controller/app/AppOrdersController.java +++ b/src/main/java/com/sqx/modules/orders/controller/app/AppOrdersController.java @@ -1,9 +1,14 @@ package com.sqx.modules.orders.controller.app; +import cn.hutool.core.util.StrUtil; import com.sqx.common.annotation.Debounce; -import com.sqx.common.annotation.Limiting; +import com.sqx.common.exception.SqxException; import com.sqx.common.utils.Result; import com.sqx.modules.app.annotation.Login; +import com.sqx.modules.app.entity.UserEntity; +import com.sqx.modules.app.service.UserService; +import com.sqx.modules.common.entity.CommonInfo; +import com.sqx.modules.common.service.CommonInfoService; import com.sqx.modules.orders.service.OrdersService; import com.sqx.modules.sys.controller.AbstractController; import io.swagger.annotations.Api; @@ -27,6 +32,22 @@ public class AppOrdersController extends AbstractController { @Autowired private OrdersService ordersService; + @Autowired + private CommonInfoService commonInfoService; + @Autowired + private UserService userService; + + private void checkWxBind(Long userId) { + CommonInfo commonInfo = commonInfoService.findOne(938); + if (commonInfo == null || !"1".equals(commonInfo.getValue())) { + return; + } + UserEntity userEntity = userService.queryByUserId(userId); + if (StrUtil.isBlank(userEntity.getWxOpenId())) { + throw new SqxException("请先绑定微信", 407); + } + } + /** * 生成商品订单 @@ -38,9 +59,9 @@ public class AppOrdersController extends AbstractController { @Login @GetMapping("/insertCourseOrders") @ApiOperation("生成商品订单") - @Debounce(value = "#userId") - @Limiting + @Debounce(interval = 20000, value = "#userId") public Result insertCourseOrders(Long courseId,Long courseDetailsId, @RequestAttribute("userId") Long userId) { + checkWxBind(userId); return ordersService.insertCourseOrders(courseId, courseDetailsId,userId); } @@ -55,6 +76,7 @@ public class AppOrdersController extends AbstractController { @GetMapping("/insertCourseOrders/limit10") @ApiOperation("生成商品订单") public Result insertCourseOrdersLimit10(Long courseId, @RequestAttribute("userId") Long userId) { + checkWxBind(userId); return ordersService.insertCourseOrdersLimit10(courseId, userId); } @@ -62,6 +84,7 @@ public class AppOrdersController extends AbstractController { @GetMapping("/insertVipOrders") @ApiOperation("生成会员订单") public Result insertVipOrders(@ApiParam("会员详情信息") Long vipDetailsId, @RequestAttribute("userId") Long userId) { + checkWxBind(userId); return ordersService.insertVipOrders(vipDetailsId, userId); } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index b5f4816c..95897d4f 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -121,66 +121,66 @@ spring: max-lifetime: ${max-lifetime} connection-timeout: ${connection-timeout} - duanju-slave: - driver-class-name: ${driver-class-name} - jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT - username: root - password: 0fd6497c308ccfa8 - minimum-idle: ${minimum-idle} - maximum-pool-size: ${maximum-pool-size} - idle-timeout: ${idle-timeout} - max-lifetime: ${max-lifetime} - connection-timeout: ${connection-timeout} - duanju-slave-0: - driver-class-name: ${driver-class-name} - jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT - username: root - password: 0fd6497c308ccfa8 - minimum-idle: ${minimum-idle} - maximum-pool-size: ${maximum-pool-size} - idle-timeout: ${idle-timeout} - max-lifetime: ${max-lifetime} - connection-timeout: ${connection-timeout} - duanju-slave-1: - driver-class-name: ${driver-class-name} - jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT - username: root - password: 0fd6497c308ccfa8 - minimum-idle: ${minimum-idle} - maximum-pool-size: ${maximum-pool-size} - idle-timeout: ${idle-timeout} - max-lifetime: ${max-lifetime} - connection-timeout: ${connection-timeout} - duanju-slave-2: - driver-class-name: ${driver-class-name} - jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT - username: root - password: 0fd6497c308ccfa8 - minimum-idle: ${minimum-idle} - maximum-pool-size: ${maximum-pool-size} - idle-timeout: ${idle-timeout} - max-lifetime: ${max-lifetime} - connection-timeout: ${connection-timeout} - duanju-slave-3: - driver-class-name: ${driver-class-name} - jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-3?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT - username: root - password: 0fd6497c308ccfa8 - minimum-idle: ${minimum-idle} - maximum-pool-size: ${maximum-pool-size} - idle-timeout: ${idle-timeout} - max-lifetime: ${max-lifetime} - connection-timeout: ${connection-timeout} - duanju-slave-4: - driver-class-name: ${driver-class-name} - jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-4?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT - username: root - password: 0fd6497c308ccfa8 - minimum-idle: ${minimum-idle} - maximum-pool-size: ${maximum-pool-size} - idle-timeout: ${idle-timeout} - max-lifetime: ${max-lifetime} - connection-timeout: ${connection-timeout} +# duanju-slave: +# driver-class-name: ${driver-class-name} +# jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +# username: root +# password: 0fd6497c308ccfa8 +# minimum-idle: ${minimum-idle} +# maximum-pool-size: ${maximum-pool-size} +# idle-timeout: ${idle-timeout} +# max-lifetime: ${max-lifetime} +# connection-timeout: ${connection-timeout} +# duanju-slave-0: +# driver-class-name: ${driver-class-name} +# jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +# username: root +# password: 0fd6497c308ccfa8 +# minimum-idle: ${minimum-idle} +# maximum-pool-size: ${maximum-pool-size} +# idle-timeout: ${idle-timeout} +# max-lifetime: ${max-lifetime} +# connection-timeout: ${connection-timeout} +# duanju-slave-1: +# driver-class-name: ${driver-class-name} +# jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +# username: root +# password: 0fd6497c308ccfa8 +# minimum-idle: ${minimum-idle} +# maximum-pool-size: ${maximum-pool-size} +# idle-timeout: ${idle-timeout} +# max-lifetime: ${max-lifetime} +# connection-timeout: ${connection-timeout} +# duanju-slave-2: +# driver-class-name: ${driver-class-name} +# jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +# username: root +# password: 0fd6497c308ccfa8 +# minimum-idle: ${minimum-idle} +# maximum-pool-size: ${maximum-pool-size} +# idle-timeout: ${idle-timeout} +# max-lifetime: ${max-lifetime} +# connection-timeout: ${connection-timeout} +# duanju-slave-3: +# driver-class-name: ${driver-class-name} +# jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-3?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +# username: root +# password: 0fd6497c308ccfa8 +# minimum-idle: ${minimum-idle} +# maximum-pool-size: ${maximum-pool-size} +# idle-timeout: ${idle-timeout} +# max-lifetime: ${max-lifetime} +# connection-timeout: ${connection-timeout} +# duanju-slave-4: +# driver-class-name: ${driver-class-name} +# jdbc-url: jdbc:mysql://47.122.26.160:3306/duanju-4?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +# username: root +# password: 0fd6497c308ccfa8 +# minimum-idle: ${minimum-idle} +# maximum-pool-size: ${maximum-pool-size} +# idle-timeout: ${idle-timeout} +# max-lifetime: ${max-lifetime} +# connection-timeout: ${connection-timeout} # 数据源配置end # 读写分离配置begin @@ -189,31 +189,31 @@ spring: duanju: masterDataSourceName: duanju slaveDataSourceNames: - # - duanju - - duanju-slave + - duanju + #- duanju-slave duanju-0: masterDataSourceName: duanju-0 slaveDataSourceNames: - # - duanju-0 - - duanju-slave-0 + - duanju-0 + #- duanju-slave-0 duanju-1: masterDataSourceName: duanju-1 slaveDataSourceNames: - # - duanju-1 - - duanju-slave-1 + - duanju-1 + #- duanju-slave-1 duanju-2: masterDataSourceName: duanju-2 slaveDataSourceNames: - # - duanju-2 - - duanju-slave-2 + - duanju-2 + #- duanju-slave-2 duanju-3: masterDataSourceName: duanju-3 slaveDataSourceNames: - # - duanju-3 - - duanju-slave-3 + - duanju-3 + #- duanju-slave-3 duanju-4: masterDataSourceName: duanju-4 slaveDataSourceNames: - # - duanju-4 - - duanju-slave-4 + - duanju-4 + #- duanju-slave-4 diff --git a/src/main/resources/db.setting b/src/main/resources/db.setting new file mode 100644 index 00000000..5213e3e5 --- /dev/null +++ b/src/main/resources/db.setting @@ -0,0 +1,24 @@ +[dev] +url = jdbc:mysql://rm-gc712o11yndj78x6a6o.mysql.cn-chengdu.rds.aliyuncs.com/duanju?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +username = video_user +password = VideoUser@1 + +[test] +url = jdbc:mysql://rm-gc712o11yndj78x6a6o.mysql.cn-chengdu.rds.aliyuncs.com/duanju?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +username = video_user +password = VideoUser@1 + +[prod] +url = jdbc:mysql://rm-gc7xx913734hv5w5q.mysql.cn-chengdu.rds.aliyuncs.com/duanju?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT +username = video_user +password = VideoUser@1 + +## 可选配置 +# 是否在日志中显示执行的SQL +showSql = true +# 是否格式化显示的SQL +formatSql = false +# 是否显示SQL参数 +showParams = true +# 打印SQL的日志等级,默认debug,可以是info、warn、error +sqlLevel = debug \ No newline at end of file diff --git a/src/main/resources/mapper/ext/ExtSysDao.xml b/src/main/resources/mapper/ext/ExtSysDao.xml new file mode 100644 index 00000000..7616eeaa --- /dev/null +++ b/src/main/resources/mapper/ext/ExtSysDao.xml @@ -0,0 +1,95 @@ + + + + + + + + + + \ No newline at end of file