| | |
| | | import com.dsh.account.model.AdvertisementChangeStateDTO; |
| | | import com.dsh.account.model.AppUserByNameAndPhoneDTO; |
| | | import com.dsh.account.model.AppUserIdsByCityName; |
| | | import com.dsh.account.model.ImageCaptchaVo; |
| | | import com.dsh.account.model.JoinPlayPaiVo; |
| | | import com.dsh.account.model.LoginSMSCodeVo; |
| | | import com.dsh.account.model.LoginWeChatVo; |
| | |
| | | |
| | | @Autowired |
| | | private TAppUserService appUserService; |
| | | @Autowired |
| | | private ImageCaptchaService imageCaptchaService; |
| | | @Autowired |
| | | private VipClient vipClient; |
| | | |
| | |
| | | } |
| | | |
| | | @ResponseBody |
| | | @PostMapping("/base/appUser/getSMSCode") |
| | | @ApiOperation(value = "获取短信验证码", tags = {"APP-登录注册"}) |
| | | @PostMapping("/base/appUser/imageCaptcha") |
| | | @ApiOperation(value = "获取短信发送前置图形验证码", tags = {"APP-登录注册"}) |
| | | @ApiImplicitParams({ |
| | | @ApiImplicitParam(value = "类型(1:登录,2:注册,3:修改密码,4:忘记密码)", name = "type", dataType = "int", required = true), |
| | | @ApiImplicitParam(value = "电话号码", name = "phone", dataType = "string", required = true) |
| | | }) |
| | | public ResultUtil getSMSCode(Integer type, String phone) { |
| | | if (ToolUtil.isEmpty(phone)) { |
| | | public ResultUtil<ImageCaptchaVo> getImageCaptcha(Integer type, String phone, HttpServletRequest request) { |
| | | if (!isValidPhone(phone)) { |
| | | return ResultUtil.paranErr("phone"); |
| | | } |
| | | if (ToolUtil.isEmpty(type)) { |
| | | if (!isValidSmsType(type)) { |
| | | return ResultUtil.paranErr("type"); |
| | | } |
| | | try { |
| | | ResultUtil smsCode = appUserService.getSMSCode(type, phone); |
| | | return smsCode; |
| | | ImageCaptchaVo captcha = imageCaptchaService.create(phone, type, getClientIp(request)); |
| | | if (captcha == null) { |
| | | return ResultUtil.error("图形验证码获取过于频繁,请稍后重试", null); |
| | | } |
| | | return ResultUtil.success(captcha); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultUtil.runErr(); |
| | | } |
| | | } |
| | | |
| | | @ResponseBody |
| | | @PostMapping("/base/appUser/getSMSCode") |
| | | @ApiOperation(value = "获取短信验证码", tags = {"APP-登录注册"}) |
| | | @ApiImplicitParams({ |
| | | @ApiImplicitParam(value = "类型(1:登录,2:注册,3:修改密码,4:忘记密码)", name = "type", dataType = "int", required = true), |
| | | @ApiImplicitParam(value = "电话号码", name = "phone", dataType = "string", required = true), |
| | | @ApiImplicitParam(value = "图形验证码唯一标识", name = "captchaId", dataType = "string", required = true), |
| | | @ApiImplicitParam(value = "图形验证码", name = "captchaCode", dataType = "string", required = true) |
| | | }) |
| | | public ResultUtil getSMSCode(Integer type, String phone, String captchaId, String captchaCode, |
| | | HttpServletRequest request) { |
| | | if (!isValidPhone(phone)) { |
| | | return ResultUtil.paranErr("phone"); |
| | | } |
| | | if (!isValidSmsType(type)) { |
| | | return ResultUtil.paranErr("type"); |
| | | } |
| | | if (ToolUtil.isEmpty(captchaId)|| ToolUtil.isEmpty(captchaCode) ) { |
| | | return ResultUtil.error("系统维护中,注册或登录请联系场地管理员"); |
| | | } |
| | | if (!imageCaptchaService.verifyAndConsume(captchaId, captchaCode, phone, type)) { |
| | | return ResultUtil.error("图形验证码错误或已失效,请刷新后重试"); |
| | | } |
| | | try { |
| | | return appUserService.getSMSCode(type, phone, getClientIp(request)); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultUtil.runErr(); |
| | | } |
| | | } |
| | | |
| | | private boolean isValidPhone(String phone) { |
| | | return phone != null && phone.matches("^1\\d{10}$"); |
| | | } |
| | | |
| | | private boolean isValidSmsType(Integer type) { |
| | | return type != null && type >= 1 && type <= 4; |
| | | } |
| | | |
| | | private String getClientIp(HttpServletRequest request) { |
| | | String remoteAddress = request.getRemoteAddr(); |
| | | if (!isInternalProxyAddress(remoteAddress)) { |
| | | return remoteAddress; |
| | | } |
| | | |
| | | String forwardedFor = request.getHeader("X-Forwarded-For"); |
| | | if (ToolUtil.isNotEmpty(forwardedFor)) { |
| | | String[] addresses = forwardedFor.split(","); |
| | | for (int i = addresses.length - 1; i >= 0; i--) { |
| | | String clientIp = addresses[i].trim(); |
| | | if (isValidIpText(clientIp) && !isInternalProxyAddress(clientIp)) { |
| | | return clientIp; |
| | | } |
| | | } |
| | | } |
| | | String realIp = request.getHeader("X-Real-IP"); |
| | | if (isValidIpText(realIp)) { |
| | | return realIp.trim(); |
| | | } |
| | | return remoteAddress; |
| | | } |
| | | |
| | | private boolean isInternalProxyAddress(String ip) { |
| | | if (ip == null) { |
| | | return false; |
| | | } |
| | | if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip) |
| | | || ip.startsWith("10.") || ip.startsWith("192.168.")) { |
| | | return true; |
| | | } |
| | | if (ip.startsWith("172.")) { |
| | | String[] parts = ip.split("\\."); |
| | | if (parts.length == 4) { |
| | | try { |
| | | int secondPart = Integer.parseInt(parts[1]); |
| | | return secondPart >= 16 && secondPart <= 31; |
| | | } catch (NumberFormatException ignored) { |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private boolean isValidIpText(String ip) { |
| | | return ToolUtil.isNotEmpty(ip) |
| | | && ip.length() <= 45 |
| | | && ip.trim().matches("[0-9a-fA-F:.]+"); |
| | | } |
| | | |
| | | |
| | | @Autowired |
| | | private TStudentService studentService; |
| New file |
| | |
| | | package com.dsh.account.model; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | |
| | | /** |
| | | * 图形验证码响应。 |
| | | */ |
| | | @ApiModel(value = "图形验证码") |
| | | public class ImageCaptchaVo { |
| | | |
| | | @ApiModelProperty(value = "验证码唯一标识") |
| | | private String captchaId; |
| | | |
| | | @ApiModelProperty(value = "PNG图片的Base64 Data URL") |
| | | private String image; |
| | | |
| | | @ApiModelProperty(value = "有效期,单位:秒") |
| | | private Integer expireIn; |
| | | |
| | | public ImageCaptchaVo(String captchaId, String image, Integer expireIn) { |
| | | this.captchaId = captchaId; |
| | | this.image = image; |
| | | this.expireIn = expireIn; |
| | | } |
| | | |
| | | public String getCaptchaId() { |
| | | return captchaId; |
| | | } |
| | | |
| | | public String getImage() { |
| | | return image; |
| | | } |
| | | |
| | | public Integer getExpireIn() { |
| | | return expireIn; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dsh.account.service; |
| | | |
| | | import com.dsh.account.model.ImageCaptchaVo; |
| | | import com.dsh.account.util.RedisUtil; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.imageio.ImageIO; |
| | | import java.awt.Color; |
| | | import java.awt.Font; |
| | | import java.awt.Graphics2D; |
| | | import java.awt.RenderingHints; |
| | | import java.awt.geom.AffineTransform; |
| | | import java.awt.image.BufferedImage; |
| | | import java.io.ByteArrayOutputStream; |
| | | import java.io.IOException; |
| | | import java.security.SecureRandom; |
| | | import java.util.Base64; |
| | | import java.util.Locale; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | | * 短信发送前置图形验证码服务。 |
| | | */ |
| | | @Service |
| | | public class ImageCaptchaService { |
| | | |
| | | private static final String CAPTCHA_KEY_PREFIX = "security:sms:image-captcha:"; |
| | | private static final String CREATE_LIMIT_KEY_PREFIX = "security:sms:image-captcha:create:"; |
| | | private static final char[] CAPTCHA_CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ".toCharArray(); |
| | | private static final int CAPTCHA_LENGTH = 4; |
| | | private static final int IMAGE_WIDTH = 120; |
| | | private static final int IMAGE_HEIGHT = 44; |
| | | |
| | | private final SecureRandom secureRandom = new SecureRandom(); |
| | | |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @Value("${security.sms.image-captcha-expire-seconds:120}") |
| | | private int expireSeconds; |
| | | |
| | | @Value("${security.sms.image-captcha-max-per-minute:10}") |
| | | private int maxPerMinute; |
| | | |
| | | /** |
| | | * 创建与手机号、短信用途绑定的图形验证码。 |
| | | * |
| | | * @return 达到生成频率上限时返回null |
| | | */ |
| | | public ImageCaptchaVo create(String phone, Integer type, String clientIp) throws IOException { |
| | | if (!redisUtil.tryAcquireFixedWindow( |
| | | CREATE_LIMIT_KEY_PREFIX + clientIp, |
| | | maxPerMinute, |
| | | 60)) { |
| | | return null; |
| | | } |
| | | |
| | | String captchaId = UUID.randomUUID().toString().replace("-", ""); |
| | | String captchaCode = randomCode(); |
| | | redisUtil.setStrValue( |
| | | CAPTCHA_KEY_PREFIX + captchaId, |
| | | captchaValue(phone, type, captchaCode), |
| | | expireSeconds |
| | | ); |
| | | return new ImageCaptchaVo(captchaId, renderImage(captchaCode), expireSeconds); |
| | | } |
| | | |
| | | /** |
| | | * 原子校验并删除验证码。无论输入是否正确,每个验证码都只能提交一次。 |
| | | */ |
| | | public boolean verifyAndConsume(String captchaId, String captchaCode, String phone, Integer type) { |
| | | if (captchaId == null || captchaCode == null) { |
| | | return false; |
| | | } |
| | | |
| | | String normalizedId = captchaId.trim().toLowerCase(Locale.ROOT); |
| | | String normalizedCode = captchaCode.trim().toUpperCase(Locale.ROOT); |
| | | if (!normalizedId.matches("[0-9a-f]{32}") || !normalizedCode.matches("[0-9A-Z]{4}")) { |
| | | return false; |
| | | } |
| | | |
| | | return redisUtil.consumeIfEquals( |
| | | CAPTCHA_KEY_PREFIX + normalizedId, |
| | | captchaValue(phone, type, normalizedCode) |
| | | ); |
| | | } |
| | | |
| | | private String randomCode() { |
| | | StringBuilder code = new StringBuilder(CAPTCHA_LENGTH); |
| | | for (int i = 0; i < CAPTCHA_LENGTH; i++) { |
| | | code.append(CAPTCHA_CHARS[secureRandom.nextInt(CAPTCHA_CHARS.length)]); |
| | | } |
| | | return code.toString(); |
| | | } |
| | | |
| | | private String renderImage(String code) throws IOException { |
| | | BufferedImage image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); |
| | | Graphics2D graphics = image.createGraphics(); |
| | | try { |
| | | graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | graphics.setColor(new Color(245, 247, 250)); |
| | | graphics.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); |
| | | drawNoise(graphics); |
| | | drawCode(graphics, code); |
| | | } finally { |
| | | graphics.dispose(); |
| | | } |
| | | |
| | | try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { |
| | | ImageIO.write(image, "png", output); |
| | | return "data:image/png;base64," + Base64.getEncoder().encodeToString(output.toByteArray()); |
| | | } |
| | | } |
| | | |
| | | private void drawNoise(Graphics2D graphics) { |
| | | for (int i = 0; i < 7; i++) { |
| | | graphics.setColor(randomColor(150, 210)); |
| | | graphics.drawLine( |
| | | secureRandom.nextInt(IMAGE_WIDTH), |
| | | secureRandom.nextInt(IMAGE_HEIGHT), |
| | | secureRandom.nextInt(IMAGE_WIDTH), |
| | | secureRandom.nextInt(IMAGE_HEIGHT) |
| | | ); |
| | | } |
| | | for (int i = 0; i < 45; i++) { |
| | | graphics.setColor(randomColor(120, 220)); |
| | | graphics.fillOval(secureRandom.nextInt(IMAGE_WIDTH), secureRandom.nextInt(IMAGE_HEIGHT), 2, 2); |
| | | } |
| | | } |
| | | |
| | | private void drawCode(Graphics2D graphics, String code) { |
| | | graphics.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 27)); |
| | | for (int i = 0; i < code.length(); i++) { |
| | | Graphics2D characterGraphics = (Graphics2D) graphics.create(); |
| | | try { |
| | | characterGraphics.setColor(randomColor(25, 115)); |
| | | double angle = (secureRandom.nextDouble() - 0.5D) * 0.45D; |
| | | int x = 12 + i * 26; |
| | | int y = 31 + secureRandom.nextInt(6) - 3; |
| | | characterGraphics.setTransform(AffineTransform.getRotateInstance(angle, x + 9, y - 10)); |
| | | characterGraphics.drawString(String.valueOf(code.charAt(i)), x, y); |
| | | } finally { |
| | | characterGraphics.dispose(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private Color randomColor(int min, int max) { |
| | | int bound = max - min + 1; |
| | | return new Color( |
| | | min + secureRandom.nextInt(bound), |
| | | min + secureRandom.nextInt(bound), |
| | | min + secureRandom.nextInt(bound) |
| | | ); |
| | | } |
| | | |
| | | private String captchaValue(String phone, Integer type, String code) { |
| | | return phone + ":" + type + ":" + code; |
| | | } |
| | | } |
| | |
| | | * |
| | | * @param type |
| | | * @param phone |
| | | * @param clientIp 客户端IP |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | | ResultUtil getSMSCode(Integer type, String phone) throws Exception; |
| | | ResultUtil getSMSCode(Integer type, String phone, String clientIp) throws Exception; |
| | | |
| | | |
| | | /** |
| | |
| | | import com.dsh.account.vo.entity.YearData; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.data.domain.Page; |
| | | import org.springframework.data.domain.PageImpl; |
| | | import org.springframework.data.domain.PageRequest; |
| | |
| | | @Service |
| | | public class TAppUserServiceImpl extends ServiceImpl<TAppUserMapper, TAppUser> implements TAppUserService { |
| | | |
| | | private static final String SMS_COOLDOWN_KEY_PREFIX = "security:sms:cooldown:"; |
| | | private static final String SMS_PHONE_QUOTA_KEY_PREFIX = "security:sms:phone-quota:"; |
| | | private static final String SMS_IP_QUOTA_KEY_PREFIX = "security:sms:ip-quota:"; |
| | | |
| | | |
| | | @Resource |
| | | private TStudentMapper tsmapper; |
| | |
| | | |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @Value("${security.sms.cooldown-seconds:60}") |
| | | private int smsCooldownSeconds; |
| | | |
| | | @Value("${security.sms.phone-max-per-day:5}") |
| | | private int smsPhoneMaxPerDay; |
| | | |
| | | @Value("${security.sms.ip-max-per-day:10}") |
| | | private int smsIpMaxPerDay; |
| | | |
| | | @Autowired |
| | | private GDMapGeocodingUtil gdMapGeocodingUtil; |
| | |
| | | * @throws Exception |
| | | */ |
| | | @Override |
| | | public ResultUtil getSMSCode(Integer type, String phone) throws Exception { |
| | | public ResultUtil getSMSCode(Integer type, String phone, String clientIp) throws Exception { |
| | | if (type == 2) { |
| | | TAppUser tAppUser = this.baseMapper.selectOne(new QueryWrapper<TAppUser>().eq("phone", phone).ne("state", 3)); |
| | | if (null != tAppUser) { |
| | | return ResultUtil.error("账号已存在"); |
| | | } |
| | | } |
| | | String cooldownKey = SMS_COOLDOWN_KEY_PREFIX + phone; |
| | | if (!redisUtil.setIfAbsent(cooldownKey, "1", smsCooldownSeconds)) { |
| | | return ResultUtil.error("短信发送过于频繁,请稍后重试"); |
| | | } |
| | | |
| | | String phoneQuotaKey = SMS_PHONE_QUOTA_KEY_PREFIX + phone; |
| | | if (!redisUtil.tryAcquireFixedWindow(phoneQuotaKey, smsPhoneMaxPerDay, 24 * 60 * 60)) { |
| | | redisUtil.remove(cooldownKey); |
| | | return ResultUtil.error("该手机号24小时内短信发送次数已达上限"); |
| | | } |
| | | |
| | | String ipQuotaKey = SMS_IP_QUOTA_KEY_PREFIX + clientIp; |
| | | if (!redisUtil.tryAcquireFixedWindow(ipQuotaKey, smsIpMaxPerDay, 24 * 60 * 60)) { |
| | | redisUtil.remove(cooldownKey); |
| | | redisUtil.releaseFixedWindow(phoneQuotaKey); |
| | | return ResultUtil.error("当前网络24小时内短信发送次数已达上限"); |
| | | } |
| | | |
| | | String numberRandom = UUIDUtil.getNumberRandom(6); |
| | | String templateCode = ""; |
| | | if (type == 1 || type == 2) { |
| | |
| | | if (type == 3 || type == 4) { |
| | | templateCode = "SMS_160960014"; |
| | | } |
| | | aLiSendSms.sendSms(phone, templateCode, "{\"code\":\"" + numberRandom + "\"}"); |
| | | redisUtil.setStrValue(phone, numberRandom, 300); |
| | | return ResultUtil.success(); |
| | | boolean providerAccepted = false; |
| | | try { |
| | | String sendResult = aLiSendSms.sendSms(phone, templateCode, "{\"code\":\"" + numberRandom + "\"}"); |
| | | com.alibaba.fastjson.JSONObject response = com.alibaba.fastjson.JSONObject.parseObject(sendResult); |
| | | if (response == null || !"OK".equalsIgnoreCase(response.getString("Code"))) { |
| | | releaseSmsLimit(cooldownKey, phoneQuotaKey, ipQuotaKey); |
| | | return ResultUtil.error("短信发送失败,请稍后重试"); |
| | | } |
| | | providerAccepted = true; |
| | | redisUtil.setStrValue(phone, numberRandom, 300); |
| | | return ResultUtil.success(); |
| | | } catch (Exception e) { |
| | | if (!providerAccepted) { |
| | | releaseSmsLimit(cooldownKey, phoneQuotaKey, ipQuotaKey); |
| | | } |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | private void releaseSmsLimit(String cooldownKey, String phoneQuotaKey, String ipQuotaKey) { |
| | | redisUtil.remove(cooldownKey); |
| | | redisUtil.releaseFixedWindow(phoneQuotaKey); |
| | | redisUtil.releaseFixedWindow(ipQuotaKey); |
| | | } |
| | | |
| | | /** |
| | |
| | | // 设置鉴权参数,初始化客户端 |
| | | private DefaultProfile profile = DefaultProfile.getProfile( |
| | | "cn-hangzhou",// 地域ID |
| | | "LTAI5tGC4cJ1rRrQsZAW2i5Z",// 您的AccessKey ID |
| | | "AwpaQbKMVYNES0SxlYYxOhU1KibCQb");// 您的AccessKey Secret |
| | | "LTAI5t6DcRqSSztaJKbeZRi1",// 您的AccessKey ID |
| | | "EP0MgGhujBiplj6QYP7FJAb5AjIjaj");// 您的AccessKey Secret |
| | | private IAcsClient client = new DefaultAcsClient(profile); |
| | | |
| | | private static void log_print(String functionName, Object result) { |
| | |
| | | public class OssUploadUtil { |
| | | //OSS图片访问域名 |
| | | public static String oss_domain = "https://newok.oss-cn-shenzhen.aliyuncs.com/"; |
| | | public static String accessKeyId = "LTAI4G9Zez9H4B36vakPXGy4"; |
| | | public static String accessKeySecret = "BOVPUeZndKVbrPOq6Ef5j6oiydB3XZ"; |
| | | public static String accessKeyId = "LTAI5t6DcRqSSztaJKbeZRi1"; |
| | | public static String accessKeySecret = "EP0MgGhujBiplj6QYP7FJAb5AjIjaj"; |
| | | public static String bucketName = "newok"; |
| | | public static String endpoint = "oss-cn-shenzhen.aliyuncs.com"; |
| | | |
| | |
| | | private String callbackPath = "https://online.daowepark.com:443/account";//支付回调网关地址/ |
| | | // private String callbackPath = "http://221.182.45.100:1234/account";//支付回调网关地址 |
| | | // private String callbackPath = "http://8.137.14.183:56666/course";//支付回调网关地址 |
| | | // private String callbackPath = "http://47.108.147.192:56666/course";//支付回调网关地址 |
| | | |
| | | private String app_cert_path = "C:/cert/alipay/user/app_cert_path.crt";//应用公钥证书路径 |
| | | |
| | |
| | | |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * 仅当key不存在时写入,并设置过期时间。 |
| | | * |
| | | * @param key key |
| | | * @param value value |
| | | * @param time 过期时间,单位:秒 |
| | | * @return 是否写入成功 |
| | | */ |
| | | public boolean setIfAbsent(String key, String value, int time) { |
| | | if (StringUtil.isEmpty(key)) { |
| | | return false; |
| | | } |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | return "OK".equals(resource.set(key, value, "NX", "EX", time)); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 原子比较并删除。key存在时无论是否匹配都会被删除,避免验证码被暴力枚举。 |
| | | */ |
| | | public boolean consumeIfEquals(String key, String expectedValue) { |
| | | if (StringUtil.isEmpty(key) || expectedValue == null) { |
| | | return false; |
| | | } |
| | | String script = "local value = redis.call('get', KEYS[1]); " |
| | | + "if not value then return 0 end; " |
| | | + "redis.call('del', KEYS[1]); " |
| | | + "if value == ARGV[1] then return 1 else return 0 end"; |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | Object result = resource.eval( |
| | | script, |
| | | Collections.singletonList(key), |
| | | Collections.singletonList(expectedValue) |
| | | ); |
| | | return Long.valueOf(1L).equals(result); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取固定时间窗口内的一个名额。 |
| | | */ |
| | | public boolean tryAcquireFixedWindow(String key, int maxCount, int windowSeconds) { |
| | | if (StringUtil.isEmpty(key) || maxCount <= 0 || windowSeconds <= 0) { |
| | | return false; |
| | | } |
| | | String script = "local current = tonumber(redis.call('get', KEYS[1]) or '0'); " |
| | | + "if current >= tonumber(ARGV[2]) then return 0 end; " |
| | | + "current = redis.call('incr', KEYS[1]); " |
| | | + "if current == 1 then redis.call('expire', KEYS[1], ARGV[1]) end; return 1"; |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | Object result = resource.eval( |
| | | script, |
| | | Collections.singletonList(key), |
| | | Arrays.asList(String.valueOf(windowSeconds), String.valueOf(maxCount)) |
| | | ); |
| | | return Long.valueOf(1L).equals(result); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 释放固定时间窗口内已获取的名额,用于下游调用失败后的补偿。 |
| | | */ |
| | | public void releaseFixedWindow(String key) { |
| | | if (StringUtil.isEmpty(key)) { |
| | | return; |
| | | } |
| | | String script = "local current = tonumber(redis.call('get', KEYS[1]) or '0'); " |
| | | + "if current <= 1 then redis.call('del', KEYS[1]); " |
| | | + "else redis.call('decr', KEYS[1]); end; return 1"; |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | resource.eval(script, Collections.singletonList(key), Collections.<String>emptyList()); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 批量获取 |
| | | * |
| | | * @param kes |
| | |
| | | #datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver |
| | | #datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_account?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai |
| | | #datasource.master0.username=root |
| | | #datasource.master0.password=root |
| | | #datasource.master0.password=123456 |
| | | #datasource.master0.maxActive=20 |
| | | #datasource.master0.maxWait=60000 |
| | | #datasource.master0.minIdle=5 |
| | |
| | | |
| | | private String callbackPath = "https://online.daowepark.com:443/activity";//支付回调网关地址 |
| | | // private String callbackPath = "http://vbef9arg13uu.guyubao.com/activity";//支付回调网关地址 |
| | | // private String callbackPath = "http://47.108.147.192:56666/activity";//支付回调网关地址 |
| | | |
| | | private String app_cert_path = "C:/cert/alipay/user/app_cert_path.crt";//应用公钥证书路径 |
| | | |
| | |
| | | select t1.insertTime,t2.cash from t_user_points_merchandise t1 |
| | | left join t_points_merchandise t2 on t1.pointsMerchandiseId = t2.id |
| | | where t1.payStatus =2 |
| | | <if test="storeIds.size()>0"> |
| | | and t1.storeId IN |
| | | <foreach collection="storeIds" separator="," item="storeId" open="(" close=")"> |
| | | #{storeId} |
| | | </foreach> |
| | | </if> |
| | | </select> |
| | | <select id="getConsumeDetails" resultType="com.dsh.activity.entity.ConsumeDetail"> |
| | | |
| | |
| | | #datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver |
| | | #datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_activity?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai |
| | | #datasource.master0.username=root |
| | | #datasource.master0.password=root |
| | | #datasource.master0.password=123456 |
| | | #datasource.master0.maxActive=20 |
| | | #datasource.master0.maxWait=60000 |
| | | #datasource.master0.minIdle=5 |
| | | #datasource.master0.initialSize=2 |
| | | |
| | | # |
| | | datasource.names=master0 |
| | | datasource.master0.type=com.alibaba.druid.pool.DruidDataSource |
| | | datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver |
| | |
| | | |
| | | private String callbackPath = "https://online.daowepark.com:443/communityWorldCup";//支付回调网关地址 |
| | | // private String callbackPath = "http://221.182.45.100:1234/communityWorldCup";//支付回调网关地址 |
| | | // private String callbackPath = "http://47.108.147.192:56666/communityWorldCup";//支付回调网关地址 |
| | | |
| | | private String app_cert_path = "C:/cert/alipay/user/app_cert_path.crt";//应用公钥证书路径 |
| | | |
| | |
| | | spring.profiles.active=fat |
| | | spring.profiles.active=prod |
| | | spring.application.name=mb-cloud-communityWorldCup |
| | | spring.cloud.nacos.username=nacos |
| | | spring.cloud.nacos.password=playPai2026#@! |
| | |
| | | #Generated by Maven |
| | | #Sun Jun 07 14:49:58 CST 2026 |
| | | #Sat Aug 15 11:48:18 CST 2026 |
| | | version=1.1.3 |
| | | groupId=cn.mb.cloud |
| | | artifactId=mb-cloud-communityWorldCup |
| | |
| | | private String key = "6f5e0c2dcabfa9c27b5da5836a362fef";//微信商户号 |
| | | |
| | | private String callbackPath = "https://online.daowepark.com:443/competition";//支付回调网关地址 |
| | | // private String callbackPath = "http://47.108.147.192:56666/competition";//支付回调网关地址 |
| | | |
| | | private String app_cert_path = "C:/cert/alipay/user/app_cert_path.crt";//应用公钥证书路径 |
| | | |
| | |
| | | // 是否启用pool的jmx管理功能, 默认true |
| | | //Enable JMX management for pool, default is set to true. |
| | | jedisPoolConfig.setJmxEnabled(true); |
| | | JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, "123456"); |
| | | JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, "playPai2026test"); |
| | | return jedisPool; |
| | | } |
| | | } |
| | |
| | | #datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver |
| | | #datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_competition?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai |
| | | #datasource.master0.username=root |
| | | #datasource.master0.password=root |
| | | #datasource.master0.password=123456 |
| | | #datasource.master0.maxActive=20 |
| | | #datasource.master0.maxWait=60000 |
| | | #datasource.master0.minIdle=5 |
| | |
| | | // 设置鉴权参数,初始化客户端 |
| | | private DefaultProfile profile = DefaultProfile.getProfile( |
| | | "cn-hangzhou",// 地域ID |
| | | "LTAI5tGC4cJ1rRrQsZAW2i5Z",// 您的AccessKey ID |
| | | "AwpaQbKMVYNES0SxlYYxOhU1KibCQb");// 您的AccessKey Secret |
| | | "LTAI5t6DcRqSSztaJKbeZRi1",// 您的AccessKey ID |
| | | "EP0MgGhujBiplj6QYP7FJAb5AjIjaj");// 您的AccessKey Secret |
| | | private IAcsClient client = new DefaultAcsClient(profile); |
| | | |
| | | private static void log_print(String functionName, Object result) { |
| | |
| | | |
| | | private String callbackPath = "https://online.daowepark.com:443/course";//支付回调网关地址 |
| | | //private String callbackPath = "http://8.137.14.183:56666/course";//支付回调网关地址 |
| | | //private String callbackPath = "http://47.108.147.192:56666/course";//支付回调网关地址 |
| | | |
| | | private String app_cert_path = "C:/cert/alipay/user/app_cert_path.crt";//应用公钥证书路径 |
| | | |
| | |
| | | #datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver |
| | | #datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_course?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai |
| | | #datasource.master0.username=root |
| | | #datasource.master0.password=root |
| | | #datasource.master0.password=123456 |
| | | #datasource.master0.maxActive=20 |
| | | #datasource.master0.maxWait=60000 |
| | | #datasource.master0.minIdle=5 |
| | | #datasource.master0.initialSize=2 |
| | | |
| | | # |
| | | datasource.names=master0 |
| | | datasource.master0.type=com.alibaba.druid.pool.DruidDataSource |
| | | datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver |
| | |
| | | //Enable JMX management for pool, default is set to true. |
| | | jedisPoolConfig.setJmxEnabled(true); |
| | | // JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, "playPai2026test"); |
| | | JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, "123456"); |
| | | JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, "playPai2026test"); |
| | | return jedisPool; |
| | | } |
| | | } |
| | |
| | | // 设置鉴权参数,初始化客户端 |
| | | private DefaultProfile profile = DefaultProfile.getProfile( |
| | | "cn-hangzhou",// 地域ID |
| | | "LTAI5tGC4cJ1rRrQsZAW2i5Z",// 您的AccessKey ID |
| | | "AwpaQbKMVYNES0SxlYYxOhU1KibCQb");// 您的AccessKey Secret |
| | | "LTAI5t6DcRqSSztaJKbeZRi1",// 您的AccessKey ID |
| | | "EP0MgGhujBiplj6QYP7FJAb5AjIjaj");// 您的AccessKey Secret |
| | | private IAcsClient client = new DefaultAcsClient(profile); |
| | | |
| | | private static void log_print(String functionName, Object result) { |
| | |
| | | public static String oss_domain = "https://we-park-life.oss-cn-beijing.aliyuncs.com/"; |
| | | // public static String accessKeyId = "LTAI5tKLeAE1B5n2mvTZ5yy6"; |
| | | // public static String accessKeySecret = "zradDLTBGdzq26tYz9RUCUh0sPmfhh"; |
| | | public static String accessKeyId = "LTAI5tGC4cJ1rRrQsZAW2i5Z"; |
| | | public static String accessKeySecret = "AwpaQbKMVYNES0SxlYYxOhU1KibCQb"; |
| | | public static String accessKeyId = "LTAI5t6DcRqSSztaJKbeZRi1"; |
| | | public static String accessKeySecret = "EP0MgGhujBiplj6QYP7FJAb5AjIjaj"; |
| | | public static String bucketName="we-park-life"; |
| | | public static String endpoint = "oss-cn-beijing.aliyuncs.com"; |
| | | |
| | |
| | | <div style="display: flex;align-items: center;justify-content: center;height: 100%"> |
| | | |
| | | |
| | | <h1 style="color: white">玩湃生活管理后台</h1> |
| | | <h1 style="color: white">玩湃生活后台管理系统V1.0</h1> |
| | | <div style="position: relative;margin-left:200px;left: -20px; max-width: 300px;min-height: 300px;background: white;border-radius: 8px;box-sizing: border-box;padding: 0 30px 0 50px;display: flex;align-items: center;justify-content: center;"> |
| | | <div style="max-height: 100%" > |
| | | <img style="margin-left: 44px" src="${ctxPath}/img/logo.png" width="100px" height="120px"/> |
| | |
| | | |
| | | private String key = "6f5e0c2dcabfa9c27b5da5836a362fef";//微信商户号 |
| | | |
| | | private String callbackPath = "https://online.daowepark.com:443/other";//支付回调网关地址 |
| | | private String callbackPath = "https://online.daowepark.com:443/other";//支付回调网关地址/ |
| | | // private String callbackPath = "http://8.137.14.183:56666/other";//支付回调网关地址 |
| | | // private String callbackPath = "http://47.108.147.192:56666/other";//支付回调网关地址 |
| | | |
| | | private String app_cert_path = "C:/cert/alipay/user/app_cert_path.crt";//应用公钥证书路径 |
| | | |