releaserelease_toaliyun_real
| @@ -13,8 +13,8 @@ | |||
| <artifactId>mallinkAdmin</artifactId> | |||
| <properties> | |||
| <weixin-java-mp.version>3.7.0</weixin-java-mp.version> | |||
| <weixin-java-open.version>3.7.0</weixin-java-open.version> | |||
| <weixin-java-mp.version>3.7.0.B</weixin-java-mp.version> | |||
| <weixin-java-open.version>3.7.0.B</weixin-java-open.version> | |||
| </properties> | |||
| <dependencies> | |||
| @@ -2,6 +2,7 @@ package com.iformall.controller.market; | |||
| import com.github.pagehelper.PageInfo; | |||
| import com.iformall.annotation.SystemControllerLog; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.controller.base.BaseController; | |||
| import com.iformall.domain.po.base.BaseEntity; | |||
| @@ -15,10 +16,7 @@ import io.swagger.annotations.ApiOperation; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.ModelAttribute; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import org.springframework.web.bind.annotation.*; | |||
| /** | |||
| * @author gongbiao | |||
| @@ -59,5 +57,27 @@ public class WxCouponPasswordController extends BaseController { | |||
| return new ResultData(page); | |||
| } | |||
| @ApiOperation(value = "卡密延期", notes = "{\"couponId\":\"string\", \"expireDate\":\"string\"}") | |||
| @PostMapping("/postpone") | |||
| @SystemControllerLog(description = "卡密延期") | |||
| public ResultData postpone(@RequestBody WxCouponPassword wxCouponPassword) { | |||
| logger.debug("[" + getIpAddr() + "] WxCouponPasswordController::postpone"); | |||
| if (wxCouponPassword.getCouponId() == null) { | |||
| logger.error("couponId不能为空: "); | |||
| return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL); | |||
| } | |||
| WxCouponPassword couponPassword = new WxCouponPassword(); | |||
| couponPassword.updateTenantInfo(getTenantInfo()); | |||
| couponPassword.setCouponId(wxCouponPassword.getCouponId()); | |||
| couponPassword.setExpireDate(wxCouponPassword.getExpireDate()); | |||
| try { | |||
| wxCouponPasswordService.postpone(couponPassword); | |||
| } catch (Exception e) { | |||
| logger.error(e.getMessage()); | |||
| return new ResultData(ErrorCode.COUPON_PASSWORD_POSTPONE_ERR); | |||
| } | |||
| return new ResultData(); | |||
| } | |||
| } | |||
| @@ -3,6 +3,7 @@ package com.iformall.log; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.baomidou.mybatisplus.annotation.TableName; | |||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||
| import com.google.common.collect.Lists; | |||
| import com.google.common.collect.MapDifference; | |||
| import com.google.common.collect.Maps; | |||
| import com.iformall.common.LogColumn; | |||
| @@ -17,7 +18,6 @@ import com.iformall.service.invest.impl.InvestBaseServiceImpl; | |||
| import com.iformall.utils.SpringContextUtils; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.commons.collections.CollectionUtils; | |||
| import org.apache.commons.compress.utils.Lists; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.apache.commons.lang3.reflect.FieldUtils; | |||
| import org.aspectj.lang.ProceedingJoinPoint; | |||
| @@ -0,0 +1,53 @@ | |||
| package com.iformall.annotation; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| import java.util.concurrent.TimeUnit; | |||
| /** | |||
| * /api/user/detail | |||
| * /api/couponOrder/getUnverifiedAmountOnDate | |||
| * /api/micropay/getMicroPayAmountOnDate | |||
| * /api/couponOrder/getVerifiedAmountOnDate | |||
| * /api/wxDateAmountRecord/listMicropay | |||
| * /api/wxDateAmountRecord/listVerified | |||
| * /api/wxDateAmountRecord/listOrder | |||
| */ | |||
| @Target({ElementType.METHOD}) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface RedisCache { | |||
| /** | |||
| * 缓存key的名称 | |||
| * @return | |||
| */ | |||
| String key() default ""; | |||
| /** | |||
| * 自动生成key | |||
| * @return | |||
| */ | |||
| boolean autoKey() default true; | |||
| /** | |||
| * 参数md5,解决key过长问题 | |||
| * @return | |||
| */ | |||
| boolean md5() default true ; | |||
| /** | |||
| * key 过期日期默认60秒 | |||
| * @return | |||
| */ | |||
| int expireTime() default 60; | |||
| /** | |||
| * 时间单位默认为秒 | |||
| * @return | |||
| */ | |||
| TimeUnit dateUnit() default TimeUnit.SECONDS; | |||
| } | |||
| @@ -0,0 +1,158 @@ | |||
| package com.iformall.aop; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.domain.po.WxBuser; | |||
| import com.iformall.exception.MallinkException; | |||
| import com.iformall.service.WxBuserTokenService; | |||
| import com.iformall.utils.HashUtil; | |||
| import com.iformall.utils.RedisLock; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.aspectj.lang.ProceedingJoinPoint; | |||
| import org.aspectj.lang.annotation.Around; | |||
| import org.aspectj.lang.annotation.Aspect; | |||
| import org.aspectj.lang.annotation.Pointcut; | |||
| import org.aspectj.lang.reflect.MethodSignature; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||
| import org.springframework.data.redis.core.ValueOperations; | |||
| import org.springframework.stereotype.Component; | |||
| import org.springframework.web.context.request.RequestContextHolder; | |||
| import org.springframework.web.context.request.ServletRequestAttributes; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import java.lang.reflect.Method; | |||
| import java.util.Arrays; | |||
| import java.util.Objects; | |||
| @Slf4j | |||
| @Aspect | |||
| @Component | |||
| public class RedisCacheAspect { | |||
| /** | |||
| * 参数分隔符 param1|param2|param3 | |||
| **/ | |||
| private static final String DELIMITER_PARAMS = "|"; | |||
| /** | |||
| * key 分隔符 | |||
| */ | |||
| private static final String DELIMITER_KEY = ":"; | |||
| @Autowired | |||
| private StringRedisTemplate stringRedisTemplate; | |||
| @Autowired | |||
| RedisLock redisLock; | |||
| @Autowired | |||
| private WxBuserTokenService userTokenService; | |||
| @Pointcut("@annotation(com.iformall.annotation.RedisCache)") | |||
| public void cacheAspect() { | |||
| } | |||
| @Around("cacheAspect()") | |||
| public Object round(ProceedingJoinPoint jp) throws Throwable { | |||
| HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); | |||
| // 查询token信息 | |||
| //从header中获取token | |||
| String token = request.getHeader("token"); | |||
| //如果header中不存在token,则从参数中获取token | |||
| if (StringUtils.isBlank(token)) { | |||
| token = request.getParameter("token"); | |||
| } | |||
| String tenantId; | |||
| WxBuser wxBuser = null; | |||
| if (StringUtils.isNotBlank(token)) { | |||
| wxBuser = userTokenService.getByToken(token); | |||
| } | |||
| if (Objects.isNull(wxBuser) || wxBuser.getExpireTime().getTime() < System.currentTimeMillis()) { | |||
| tenantId = "0"; | |||
| } else { | |||
| tenantId = wxBuser.getTenantId(); | |||
| } | |||
| // 请求参数 | |||
| Object[] args = jp.getArgs(); | |||
| // 接口 返回结果 | |||
| Object result; | |||
| String redisKey; | |||
| //得到注释的名称 | |||
| MethodSignature signature = (MethodSignature) jp.getSignature(); | |||
| //判断tag是否在用户权限中 如果存在加入参数查询 | |||
| Method method = signature.getMethod(); | |||
| RedisCache cache = method.getAnnotation(RedisCache.class) ; | |||
| if (cache.autoKey()) { | |||
| // 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3) | |||
| String requestUrl = request.getRequestURI(); | |||
| // 接口路径 reqPath => /api/xxx | |||
| String reqPath = requestUrl.substring(requestUrl.indexOf("/api/")); | |||
| if (cache.md5()) { | |||
| redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, HashUtil.md5(StringUtils.join(args, DELIMITER_PARAMS))); | |||
| } else { | |||
| redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, StringUtils.join(args, DELIMITER_PARAMS)); | |||
| } | |||
| } else { | |||
| redisKey = cache.key(); | |||
| } | |||
| // redisKey 不存在,获取接口数据并返回 | |||
| if (StringUtils.isBlank(redisKey)) { | |||
| result = jp.proceed(args); | |||
| } else { | |||
| ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue(); | |||
| String value = valueOperations.get(redisKey); | |||
| if (value == null) { | |||
| String lockKey = StringUtils.join(redisKey, ":", "lock"); | |||
| long time = System.currentTimeMillis() + 2000; | |||
| String timeStr = String.valueOf(time); | |||
| try { | |||
| //分布式锁,保证一个线程读DB,其它线程排队 | |||
| if (redisLock.lock2(lockKey, timeStr)) { | |||
| log.debug("CacheAspect 读库中, key:{}: " + lockKey); | |||
| result = jp.proceed(args); | |||
| String json_data = JSONObject.toJSONString(result); | |||
| valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit()); | |||
| log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime()); | |||
| } else { | |||
| log.debug("CacheAspect 读库等待中, key:{}: " + lockKey); | |||
| Thread.sleep(2000); | |||
| value = valueOperations.get(redisKey); | |||
| result = parseCache(jp, redisKey, value); | |||
| } | |||
| } catch (Throwable throwable) { | |||
| log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()), | |||
| jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable); | |||
| if (throwable instanceof MallinkException) { | |||
| throw throwable; | |||
| } | |||
| throw new MallinkException(ErrorCode.SYS_SERVER_ERROR); | |||
| } finally { | |||
| redisLock.unlock(lockKey, timeStr); | |||
| } | |||
| } else { | |||
| result = parseCache(jp, redisKey, value); | |||
| } | |||
| // 计数器 | |||
| stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1); | |||
| } | |||
| return result; | |||
| } | |||
| private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) { | |||
| if (StringUtils.isBlank(value)) { | |||
| log.debug("CacheAspect 缓存存在,解析缓存数据为空:{},key:{}", value, redisKey); | |||
| return null; | |||
| } | |||
| Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType(); | |||
| log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey); | |||
| return JSONObject.parseObject(value, returnType); | |||
| } | |||
| } | |||
| @@ -16,6 +16,8 @@ import org.springframework.data.redis.cache.RedisCacheManager; | |||
| import org.springframework.data.redis.cache.RedisCacheWriter; | |||
| import org.springframework.data.redis.connection.RedisConnectionFactory; | |||
| import org.springframework.data.redis.core.RedisTemplate; | |||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||
| import org.springframework.data.redis.core.ValueOperations; | |||
| import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | |||
| import org.springframework.data.redis.serializer.StringRedisSerializer; | |||
| @@ -172,4 +174,12 @@ public class RedisConfig extends CachingConfigurerSupport { | |||
| template.setConnectionFactory(connectionFactory); | |||
| return template; | |||
| } | |||
| @Bean("stringValueOperations") | |||
| public ValueOperations<String, String> getStringValueOperations(RedisConnectionFactory connectionFactory) { | |||
| StringRedisTemplate template = new StringRedisTemplate(); | |||
| template.setConnectionFactory(connectionFactory); | |||
| template.afterPropertiesSet(); | |||
| return template.opsForValue(); | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package com.iformall.controller; | |||
| import com.github.pagehelper.PageInfo; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.Result; | |||
| import com.iformall.common.ResultData; | |||
| @@ -86,6 +87,7 @@ public class WxCouponOrderController extends BaseController { | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("某日期交易总流水") | |||
| @GetMapping("getUnverifiedAmountOnDate") | |||
| @ApiImplicitParams({ | |||
| @@ -94,6 +96,7 @@ public class WxCouponOrderController extends BaseController { | |||
| return wxCouponOrderService.listOnDateAsPage(getUser().getId(), date, null, null, false); | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("某日期核销总流水") | |||
| @GetMapping("getVerifiedAmountOnDate") | |||
| @ApiImplicitParams({ | |||
| @@ -1,6 +1,7 @@ | |||
| package com.iformall.controller; | |||
| import com.github.pagehelper.PageInfo; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.base.BaseEntity; | |||
| import com.iformall.domain.po.WxDateAmountRecord; | |||
| @@ -31,19 +32,21 @@ public class WxDateAmountRecordController extends BaseController { | |||
| @Autowired | |||
| private WxDateAmountRecordService wxDateAmountRecordService; | |||
| @RedisCache | |||
| @ApiOperation("首页查询交易金额记录接口") | |||
| @GetMapping("listOrder") | |||
| public ResultData listOrder() { | |||
| return list(EnumDateAmtType.PAY_RECORD.getCode()); | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("首页查询核销金额记录接口") | |||
| @GetMapping("listVerified") | |||
| public ResultData listVerified() { | |||
| return list(EnumDateAmtType.VERIFY_RECORD.getCode()); | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("首页查询刷卡金额记录接口") | |||
| @GetMapping("listMicropay") | |||
| public ResultData listMicropay() { | |||
| @@ -2,6 +2,7 @@ package com.iformall.controller; | |||
| import com.iformall.annotation.AuthIgnore; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.WxMall; | |||
| @@ -55,6 +56,7 @@ public class WxMerchantBUserController extends BaseController { | |||
| @Autowired | |||
| private WxScoreRulesService wxScoreRulesService; | |||
| @RedisCache | |||
| @ApiOperation("查寻当bUser详情接口") | |||
| @GetMapping("/detail") | |||
| public ResultData detail() { | |||
| @@ -2,11 +2,9 @@ package com.iformall.controller; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.WxBuser; | |||
| import com.iformall.domain.po.WxMerchant; | |||
| import com.iformall.domain.po.WxMerchantBUser; | |||
| import com.iformall.domain.po.WxProfitSharingReceiver; | |||
| import com.iformall.domain.po.*; | |||
| import com.iformall.enums.EnumMsgSend; | |||
| import com.iformall.service.WxCUserService; | |||
| import com.iformall.service.WxMerchantService; | |||
| import com.iformall.service.WxProfitSharingReceiverService; | |||
| import io.swagger.annotations.Api; | |||
| @@ -29,6 +27,9 @@ public class WxMerchantController extends BaseController { | |||
| @Autowired | |||
| private WxProfitSharingReceiverService wxProfitSharingReceiverService; | |||
| @Autowired | |||
| private WxCUserService cUserService; | |||
| @ApiOperation("查询当前租户下商户名称列表") | |||
| @GetMapping("/name_list") | |||
| public ResultData nameList() { | |||
| @@ -97,6 +98,24 @@ public class WxMerchantController extends BaseController { | |||
| return new ResultData(has); | |||
| } | |||
| @ApiOperation("获取微信OpenId") | |||
| @GetMapping("/getOpenIdByPhone") | |||
| @ApiImplicitParam(name = "phone", value = "商户C端注册手机号", dataType = "String", paramType = "query", required = true) | |||
| public ResultData getOpenIdByPhone(@ModelAttribute String phone) { | |||
| log.debug("[" + getIpAddr() + "] WxMerchantController::getOpenIdByPhone"); | |||
| if (!hasPermission()) { | |||
| return new ResultData(ErrorCode.USER_NO_PERMISSION); | |||
| } | |||
| if (StringUtils.isBlank(phone)) { | |||
| return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "商户C端注册手机号不能为空"); | |||
| } | |||
| WxCUser cuUser = new WxCUser(); | |||
| cuUser.updateTenantInfo(getTenantInfo()); | |||
| cuUser.setPhone(phone); | |||
| WxCUser mUser = cUserService.getByObject(cuUser); | |||
| return new ResultData(mUser.getOpenId()); | |||
| } | |||
| public boolean hasPermission() { | |||
| WxMerchantBUser user = getUser(); | |||
| WxBuser buser = getBuser(); | |||
| @@ -2,6 +2,7 @@ package com.iformall.controller; | |||
| import com.alibaba.fastjson.JSONArray; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.*; | |||
| @@ -225,6 +226,7 @@ public class WxMicroPayController extends BaseController { | |||
| } | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("付款码支付交易流水分页列表接口") | |||
| @GetMapping("listMicroPay") | |||
| @ApiImplicitParams({ | |||
| @@ -235,6 +237,7 @@ public class WxMicroPayController extends BaseController { | |||
| return wxOrderService.microPayListOnDateAsPage(getUser().getId(), EnumOrderType.MICROPAY.getCode(), date, pageNum, pageSize); | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("付款码支付某日期交易总流水") | |||
| @GetMapping("getMicroPayAmountOnDate") | |||
| @ApiImplicitParams({ | |||
| @@ -243,6 +246,16 @@ public class WxMicroPayController extends BaseController { | |||
| return wxOrderService.microPayListOnDateAsPage(getUser().getId(), EnumOrderType.MICROPAY.getCode(), date, null, null); | |||
| } | |||
| @ApiOperation("付款码支付某日异常交易分页列表接口") | |||
| @GetMapping("listMicroPayExp") | |||
| @ApiImplicitParams({ | |||
| @ApiImplicitParam(name = "date", value = "日期yyyy-MM-dd", dataType = "string", paramType = "query"), | |||
| @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "int", paramType = "query",required = true), | |||
| @ApiImplicitParam(name = "pageSize", value = "每页条数", dataType = "int", paramType = "query",required = true)}) | |||
| public ResultData listMicroPayExp(String date, Integer pageNum, Integer pageSize) { | |||
| return wxOrderService.microPayExpListOnDateAsPage(getUser().getId(), EnumOrderType.MICROPAY.getCode(), date, pageNum, pageSize); | |||
| } | |||
| @ApiOperation(value = "付款码支付退款", notes = "{\"orderId\":\"string\"}") | |||
| @PostMapping("/microPayRefund") | |||
| @@ -0,0 +1,53 @@ | |||
| package com.iformall.annotation; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| import java.util.concurrent.TimeUnit; | |||
| /** | |||
| * - /api/wxCouponChannel/change | |||
| * - /api/user/userinfo | |||
| * - /api/wxBusiness/listAll | |||
| * - /api/wxCampaign/list | |||
| * - /api/mall/mallInfo | |||
| * - /api/mall/getAppIcon | |||
| * - /api/mall/getWeapNote | |||
| */ | |||
| @Target({ElementType.METHOD}) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface RedisCache { | |||
| /** | |||
| * 缓存key的名称 | |||
| * @return | |||
| */ | |||
| String key() default ""; | |||
| /** | |||
| * 自动生成key | |||
| * @return | |||
| */ | |||
| boolean autoKey() default true; | |||
| /** | |||
| * 参数md5,解决key过长问题 | |||
| * @return | |||
| */ | |||
| boolean md5() default true ; | |||
| /** | |||
| * key 过期日期默认60秒 | |||
| * @return | |||
| */ | |||
| int expireTime() default 60; | |||
| /** | |||
| * 时间单位默认为秒 | |||
| * @return | |||
| */ | |||
| TimeUnit dateUnit() default TimeUnit.SECONDS; | |||
| } | |||
| @@ -0,0 +1,152 @@ | |||
| package com.iformall.aop; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.domain.po.WxCUser; | |||
| import com.iformall.service.WxCUserTokenService; | |||
| import com.iformall.utils.HashUtil; | |||
| import com.iformall.utils.RedisLock; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.aspectj.lang.ProceedingJoinPoint; | |||
| import org.aspectj.lang.annotation.Around; | |||
| import org.aspectj.lang.annotation.Aspect; | |||
| import org.aspectj.lang.annotation.Pointcut; | |||
| import org.aspectj.lang.reflect.MethodSignature; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||
| import org.springframework.data.redis.core.ValueOperations; | |||
| import org.springframework.stereotype.Component; | |||
| import org.springframework.web.context.request.RequestContextHolder; | |||
| import org.springframework.web.context.request.ServletRequestAttributes; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import java.lang.reflect.Method; | |||
| import java.util.Arrays; | |||
| import java.util.Objects; | |||
| @Slf4j | |||
| @Aspect | |||
| @Component | |||
| public class RedisCacheAspect { | |||
| /** | |||
| * 参数分隔符 param1|param2|param3 | |||
| **/ | |||
| private static final String DELIMITER_PARAMS = "|"; | |||
| /** | |||
| * key 分隔符 | |||
| */ | |||
| private static final String DELIMITER_KEY = ":"; | |||
| @Autowired | |||
| private StringRedisTemplate stringRedisTemplate; | |||
| @Autowired | |||
| RedisLock redisLock; | |||
| @Autowired | |||
| private WxCUserTokenService userTokenService; | |||
| @Pointcut("@annotation(com.iformall.annotation.RedisCache)") | |||
| public void cacheAspect() { | |||
| } | |||
| @Around("cacheAspect()") | |||
| public Object round(ProceedingJoinPoint jp) throws Throwable { | |||
| HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); | |||
| // 查询token信息 | |||
| //从header中获取token | |||
| String token = request.getHeader("token"); | |||
| //如果header中不存在token,则从参数中获取token | |||
| if (StringUtils.isBlank(token)) { | |||
| token = request.getParameter("token"); | |||
| } | |||
| String tenantId; | |||
| WxCUser wxCUser = null ; | |||
| if(StringUtils.isNotBlank(token)) { | |||
| wxCUser = userTokenService.getByToken(token); | |||
| } | |||
| if (Objects.isNull(wxCUser) || wxCUser.getExpireTime().getTime() < System.currentTimeMillis()) { | |||
| tenantId = "0"; | |||
| } else { | |||
| tenantId = wxCUser.getTenantId(); | |||
| } | |||
| // 请求参数 | |||
| Object[] args = jp.getArgs(); | |||
| // 接口 返回结果 | |||
| Object result; | |||
| String redisKey; | |||
| //得到注释的名称 | |||
| MethodSignature signature = (MethodSignature) jp.getSignature(); | |||
| //判断tag是否在用户权限中 如果存在加入参数查询 | |||
| Method method = signature.getMethod(); | |||
| RedisCache cache = method.getAnnotation(RedisCache.class) ; | |||
| if (cache.autoKey()) { | |||
| // 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3) | |||
| String requestUrl = request.getRequestURI(); | |||
| // 接口路径 reqPath => /api/xxx | |||
| String reqPath = requestUrl.substring(requestUrl.indexOf("/api/")); | |||
| if (cache.md5()) { | |||
| redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, HashUtil.md5(StringUtils.join(args, DELIMITER_PARAMS))); | |||
| } else { | |||
| redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, StringUtils.join(args, DELIMITER_PARAMS)); | |||
| } | |||
| } else { | |||
| redisKey = cache.key(); | |||
| } | |||
| // redisKey 不存在,获取接口数据并返回 | |||
| if (StringUtils.isBlank(redisKey)) { | |||
| result = jp.proceed(args); | |||
| } else { | |||
| ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue(); | |||
| String value = valueOperations.get(redisKey); | |||
| if (value == null) { | |||
| String lockKey = StringUtils.join(redisKey, ":", "lock"); | |||
| long time = System.currentTimeMillis() + 2000; | |||
| String timeStr = String.valueOf(time); | |||
| try { | |||
| //分布式锁,保证一个线程读DB,其它线程排队 | |||
| if (redisLock.lock2(lockKey, timeStr)) { | |||
| log.debug("CacheAspect 读库中, key:{}: " + lockKey); | |||
| result = jp.proceed(args); | |||
| String json_data = JSONObject.toJSONString(result); | |||
| valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit()); | |||
| log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime()); | |||
| } else { | |||
| log.debug("CacheAspect 读库等待中, key:{}: " + lockKey); | |||
| Thread.sleep(2000); | |||
| value = valueOperations.get(redisKey); | |||
| result = parseCache(jp, redisKey, value); | |||
| } | |||
| } catch (Throwable throwable) { | |||
| log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()), | |||
| jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable); | |||
| throw throwable; | |||
| } finally { | |||
| redisLock.unlock(lockKey, timeStr); | |||
| } | |||
| } else { | |||
| result = parseCache(jp, redisKey, value); | |||
| } | |||
| // 计数器 | |||
| stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1); | |||
| } | |||
| return result; | |||
| } | |||
| private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) { | |||
| if (StringUtils.isBlank(value)) { | |||
| log.debug("CacheAspect 缓存存在,解析缓存数据为空:{},key:{}", value, redisKey); | |||
| return null; | |||
| } | |||
| Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType(); | |||
| log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey); | |||
| return JSONObject.parseObject(value, returnType); | |||
| } | |||
| } | |||
| @@ -16,6 +16,8 @@ import org.springframework.data.redis.cache.RedisCacheManager; | |||
| import org.springframework.data.redis.cache.RedisCacheWriter; | |||
| import org.springframework.data.redis.connection.RedisConnectionFactory; | |||
| import org.springframework.data.redis.core.RedisTemplate; | |||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||
| import org.springframework.data.redis.core.ValueOperations; | |||
| import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | |||
| import org.springframework.data.redis.serializer.StringRedisSerializer; | |||
| @@ -174,4 +176,31 @@ public class RedisConfig extends CachingConfigurerSupport { | |||
| template.setConnectionFactory(connectionFactory); | |||
| return template; | |||
| } | |||
| @Bean("pressOrderRedisTemplate") | |||
| public RedisTemplate<String, WxOrder> getPressOrderRedisTemplate(RedisConnectionFactory connectionFactory) { | |||
| RedisTemplate<String, WxOrder> template = new RedisTemplate<>(); | |||
| Jackson2JsonRedisSerializer<WxOrder> j = new Jackson2JsonRedisSerializer(WxOrder.class); | |||
| // value值的序列化 | |||
| template.setValueSerializer(j); | |||
| template.setHashKeySerializer(j); | |||
| // key的序列化 | |||
| template.setKeySerializer(new StringRedisSerializer()); | |||
| template.setHashKeySerializer(new StringRedisSerializer()); | |||
| template.setConnectionFactory(connectionFactory); | |||
| return template; | |||
| } | |||
| @Bean("stringValueOperations") | |||
| public ValueOperations<String, String> getStringValueOperations(RedisConnectionFactory connectionFactory) { | |||
| StringRedisTemplate template = new StringRedisTemplate(); | |||
| template.setConnectionFactory(connectionFactory); | |||
| template.afterPropertiesSet(); | |||
| return template.opsForValue(); | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package com.iformall.controller; | |||
| import com.github.pagehelper.PageInfo; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.WxBusiness; | |||
| import com.iformall.domain.po.WxCUser; | |||
| @@ -26,6 +27,7 @@ public class WxBusinessController extends BaseController { | |||
| @Autowired | |||
| private WxBusinessService wxBusinessService; | |||
| @RedisCache | |||
| @ApiOperation("业态全列表接口") | |||
| @GetMapping("listAll") | |||
| public ResultData listAll(Integer filter) { | |||
| @@ -1,6 +1,7 @@ | |||
| package com.iformall.controller; | |||
| import com.github.pagehelper.PageInfo; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.Result; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.base.BaseEntity; | |||
| @@ -38,6 +39,7 @@ public class WxCampaignController extends BaseController { | |||
| @Autowired | |||
| private WxCouponChannelService wxCouponChannelService; | |||
| @RedisCache | |||
| @ApiOperation("分页列表接口") | |||
| @GetMapping("list") | |||
| @ApiImplicitParams({ | |||
| @@ -704,10 +704,14 @@ public class WxCarController extends BaseController { | |||
| String unionId = objParams.getString(BoLinkUtil.BOLINK_UNION_ID); | |||
| String key = objParams.getString(BoLinkUtil.BOLINK_KEY); | |||
| String ret = BoLinkUtil.getStopFee(url, comid, unionId, key, carNumber); | |||
| JSONObject retObj = JSON.parseObject(ret); | |||
| JSONObject dataObj = retObj.getJSONObject(BoLinkUtil.BOLINK_DATA); | |||
| if (dataObj.getIntValue(BoLinkUtil.BOLINK_STATE) == BoLinkUtil.BOLINK_SUCCESS) { | |||
| return new ResultData(dataObj); | |||
| if (ret != null) { | |||
| JSONObject retObj = JSON.parseObject(ret); | |||
| JSONObject dataObj = retObj.getJSONObject(BoLinkUtil.BOLINK_DATA); | |||
| if (dataObj.getIntValue(BoLinkUtil.BOLINK_STATE) == BoLinkUtil.BOLINK_SUCCESS) { | |||
| return new ResultData(dataObj); | |||
| } else { | |||
| return new ResultData(ErrorCode.BOLINK_STOP_FEE_FAIL); | |||
| } | |||
| } else { | |||
| return new ResultData(ErrorCode.BOLINK_STOP_FEE_FAIL); | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package com.iformall.controller; | |||
| import com.github.pagehelper.PageInfo; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.base.BaseEntity; | |||
| import com.iformall.domain.po.WxCouponChannel; | |||
| @@ -97,7 +98,7 @@ public class WxCouponChannelController extends BaseController { | |||
| } | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("分页列表接口") | |||
| @GetMapping("change") | |||
| public ResultData change(@ModelAttribute WxCouponChannel wxCouponChannel) { | |||
| @@ -0,0 +1,69 @@ | |||
| package com.iformall.controller; | |||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.*; | |||
| import com.iformall.exception.MallinkException; | |||
| import io.swagger.annotations.Api; | |||
| import io.swagger.annotations.ApiOperation; | |||
| import me.chanjar.weixin.common.error.WxErrorException; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import java.util.concurrent.TimeUnit; | |||
| @RestController | |||
| @RequestMapping("/api/live") | |||
| @Api(tags = "微信直播相关接口") | |||
| public class WxLiveController extends BaseController { | |||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||
| /** | |||
| * 获取所有直播间和商品信息(没有分页直接获取全部) | |||
| * | |||
| * @return | |||
| */ | |||
| @ApiOperation("获取所有直播间和商品信息") | |||
| @RedisCache(expireTime = 30,dateUnit = TimeUnit.MINUTES) | |||
| @GetMapping("/roomList") | |||
| public ResultData getLiveinfos(String appId) { | |||
| ResultData resultData = new ResultData(); | |||
| try { | |||
| WxMaService wxMaService = getWeappService(appId); | |||
| resultData.data = wxMaService.getLiveService().getLiveinfos(); | |||
| } catch (WxErrorException e) { | |||
| logger.error("getLiveinfos error", e); | |||
| throw new MallinkException(ErrorCode.SYS_SERVER_ERROR); | |||
| } | |||
| return resultData; | |||
| } | |||
| /** | |||
| * 获取直播房间回放数据信息. | |||
| * <p> | |||
| * 获取回放 (默认:get_replay) | |||
| * | |||
| * @param room_id 直播间 id | |||
| * @param start 起始拉取视频,start = 0 表示从第 1 个视频片段开始拉取 | |||
| * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 | |||
| * @return | |||
| */ | |||
| @ApiOperation("获取直播房间回放数据和商品信息") | |||
| @RedisCache(expireTime = 30,dateUnit = TimeUnit.MINUTES) | |||
| @GetMapping("/replayList") | |||
| public ResultData getLiveReplay(Integer room_id, Integer start, Integer limit) { | |||
| ResultData resultData = new ResultData(); | |||
| WxCUser user = getUser(); | |||
| try { | |||
| WxMaService wxMaService = getWeappService(user.getAppId()); | |||
| resultData.data = wxMaService.getLiveService().getLiveReplay(room_id,start,limit); | |||
| } catch (WxErrorException e) { | |||
| logger.error("getLiveReplay error", e); | |||
| throw new MallinkException(ErrorCode.SYS_SERVER_ERROR); | |||
| } | |||
| return resultData; | |||
| } | |||
| } | |||
| @@ -1,13 +1,12 @@ | |||
| package com.iformall.controller; | |||
| import com.iformall.annotation.AuthIgnore; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.Result; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.*; | |||
| import com.iformall.domain.po.base.TenantEntity; | |||
| import com.iformall.domain.vo.WxMerchantVo; | |||
| import com.iformall.enums.EnumMerchantStatus; | |||
| import com.iformall.service.*; | |||
| import io.swagger.annotations.Api; | |||
| import io.swagger.annotations.ApiImplicitParam; | |||
| @@ -16,13 +15,9 @@ import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.*; | |||
| @RestController | |||
| @RequestMapping("/api/mall") | |||
| @@ -45,6 +40,10 @@ public class WxMallController extends BaseController { | |||
| @Autowired | |||
| private WxLevelConfigService wxLevelConfigService; | |||
| @Autowired | |||
| private RedisService redisService; | |||
| @RedisCache | |||
| @AuthIgnore | |||
| @ApiOperation("商场图标") | |||
| @GetMapping("/getAppIcon") | |||
| @@ -64,6 +63,7 @@ public class WxMallController extends BaseController { | |||
| return new ResultData(Result.SUCCESS, "查询成功", resultMap); | |||
| } | |||
| @RedisCache | |||
| @AuthIgnore | |||
| @ApiOperation("商场提示信息") | |||
| @GetMapping("/getWeapNote") | |||
| @@ -82,6 +82,7 @@ public class WxMallController extends BaseController { | |||
| return new ResultData(Result.SUCCESS, "查询成功", resultMap); | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("商场信息") | |||
| @GetMapping("/mallInfo") | |||
| public ResultData getMallInfo() { | |||
| @@ -113,7 +114,7 @@ public class WxMallController extends BaseController { | |||
| return new ResultData(Result.SUCCESS, "查询成功", map); | |||
| } | |||
| @RedisCache() | |||
| @AuthIgnore | |||
| @ApiOperation("获取后端版本号") | |||
| @GetMapping("/version") | |||
| @@ -121,5 +122,16 @@ public class WxMallController extends BaseController { | |||
| return new ResultData(version); | |||
| } | |||
| @ApiOperation("获取热点缓存接口对应redis key") | |||
| @GetMapping("/hotApis") | |||
| public ResultData hotApis() { | |||
| return new ResultData(redisService.hotApi()); | |||
| } | |||
| @ApiOperation("删除指定key") | |||
| @PostMapping("/delRedisKey") | |||
| public ResultData delRedisKey(@RequestBody Collection<Object> keys) { | |||
| return new ResultData(redisService.delRedisKey(keys)); | |||
| } | |||
| } | |||
| @@ -2,15 +2,16 @@ package com.iformall.controller; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.domain.po.WxCUser; | |||
| import com.iformall.domain.po.WxCoupon; | |||
| import com.iformall.domain.po.WxOrder; | |||
| import com.iformall.domain.po.WxOrderPress; | |||
| import com.iformall.domain.vo.WxCouponCVo; | |||
| import com.iformall.enums.EnumOrderPressStatus; | |||
| import com.iformall.enums.EnumOrderStatus; | |||
| import com.iformall.exception.MallinkException; | |||
| import com.iformall.service.WxCouponService; | |||
| import com.iformall.service.WxOrderPressService; | |||
| import com.iformall.service.WxOrderService; | |||
| import com.iformall.utils.RedisLock; | |||
| import io.swagger.annotations.Api; | |||
| import io.swagger.annotations.ApiImplicitParam; | |||
| import io.swagger.annotations.ApiOperation; | |||
| @@ -18,11 +19,16 @@ import org.apache.commons.lang3.StringUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Qualifier; | |||
| import org.springframework.data.redis.core.RedisTemplate; | |||
| import org.springframework.data.redis.core.ValueOperations; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import java.util.Date; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import java.util.concurrent.TimeUnit; | |||
| @RestController | |||
| @RequestMapping("/api/press") | |||
| @@ -39,6 +45,17 @@ public class WxPressOrderController extends BaseController { | |||
| @Autowired | |||
| WxOrderPressService wxOrderPressService; | |||
| @Autowired | |||
| @Qualifier("pressOrderRedisTemplate") | |||
| RedisTemplate<String, WxOrder> orderRedisTemplate; | |||
| @Autowired | |||
| @Qualifier("couponDetailRedisTemplate") | |||
| RedisTemplate<String, WxCouponCVo> couponCVoRedisTemplate; | |||
| @Autowired | |||
| RedisLock redisLock; | |||
| // 砍价列表 - /api/wxCouponChannel/list | |||
| // 我的砍价 - /api/order/pressOrderList | |||
| // 砍价订单详情 - /api/order/pressOrderDetail | |||
| @@ -49,6 +66,7 @@ public class WxPressOrderController extends BaseController { | |||
| @ApiOperation(value = "参与砍价", notes = "{\"orderId\":\"String\"}") | |||
| @PostMapping("pressOrderJoin") | |||
| @Transactional | |||
| public ResultData pressOrderJoin(@RequestBody Map<String, String> paramMap) { | |||
| Date curDate = new Date(); | |||
| String orderIdStr = paramMap.get("orderId"); | |||
| @@ -56,19 +74,50 @@ public class WxPressOrderController extends BaseController { | |||
| return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "orderId不能为空"); | |||
| } | |||
| Long orderId = 0L; | |||
| WxOrder order = null; | |||
| try { | |||
| orderId = Long.valueOf(orderIdStr); | |||
| } catch (NumberFormatException e) { | |||
| logger.error("orderId convert error, " + orderIdStr + ", e:" + e.getMessage()); | |||
| return new ResultData(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "orderId: " + orderIdStr + ", e:" + e.getMessage()); | |||
| } | |||
| WxOrder order = wxOrderService.getById(orderId); | |||
| String key = "pressorder:" + orderId; | |||
| ValueOperations<String, WxOrder> operations = orderRedisTemplate.opsForValue(); | |||
| // 缓存 | |||
| boolean hasKey = orderRedisTemplate.hasKey(key); | |||
| if (hasKey) { | |||
| // 从缓存获取砍价订单信息 | |||
| order = operations.get(key); | |||
| } else { | |||
| // 订单没有入缓存,需要从数据库中读取 | |||
| order = wxOrderService.getById(orderId); | |||
| if (order != null) { | |||
| // 订单优化,进缓存 | |||
| orderRedisTemplate.opsForValue().set(key, order, 3600, TimeUnit.SECONDS); | |||
| } | |||
| } | |||
| if(order == null) { | |||
| logger.error("订单不存在:" + orderId); | |||
| return new ResultData(ErrorCode.ORDER_IS_NOT_FIND); | |||
| } | |||
| WxCoupon coupon = wxCouponService.getById(order.getProductId()); | |||
| if(coupon == null) { | |||
| WxCouponCVo wxCouponCVo = null; | |||
| String cdKey = "cd:" + order.getProductId(); | |||
| ValueOperations<String, WxCouponCVo> cdOperations = couponCVoRedisTemplate.opsForValue(); | |||
| // 缓存 | |||
| boolean cdHasKey = couponCVoRedisTemplate.hasKey(cdKey); | |||
| if (cdHasKey) { | |||
| // 从缓存获取券信息 | |||
| wxCouponCVo = cdOperations.get(cdKey); | |||
| } else { | |||
| // 券信息没有入缓存,需要从数据库中读取 | |||
| wxCouponCVo = wxCouponService.getVoById(order.getProductId()); | |||
| if (wxCouponCVo != null) { | |||
| // 券详情优化,进缓存 | |||
| couponCVoRedisTemplate.opsForValue().set(cdKey, wxCouponCVo, 3600, TimeUnit.SECONDS); | |||
| } | |||
| } | |||
| if(wxCouponCVo == null) { | |||
| logger.error("券不存在:" + order.getProductId()); | |||
| return new ResultData(ErrorCode.COUPON_IS_EMPTY); | |||
| } | |||
| @@ -93,21 +142,86 @@ public class WxPressOrderController extends BaseController { | |||
| return new ResultData(ErrorCode.ORDER_HAD_CANCEL); | |||
| } | |||
| if(order.getPressCurrentNum() + 1 > coupon.getPressLimitNum()) { | |||
| if(order.getPressCurrentNum() + 1 > wxCouponCVo.getPressLimitNum()) { | |||
| logger.error("砍价已结束:" + orderId); | |||
| return new ResultData(ErrorCode.COUPON_PRESS_HAD_FINISHED); | |||
| } | |||
| long time = System.currentTimeMillis() + RedisLock.TIMEOUT; | |||
| String timeStr = String.valueOf(time); | |||
| if (!redisLock.lock(orderIdStr, timeStr)) { | |||
| logger.error("此砍价订单被锁定, orderId: " + orderIdStr); | |||
| return new ResultData(ErrorCode.TOO_MANY_REQUEST); | |||
| } | |||
| Long cUserId = getUserId(); | |||
| // 检查此人是否已参与砍价 | |||
| boolean hadJoin = wxOrderPressService.checkCUserHasJoin(order, cUserId); | |||
| if (hadJoin) { | |||
| redisLock.unlock(orderIdStr, timeStr); | |||
| logger.error("用户已参与砍价"); | |||
| return new ResultData(ErrorCode.COUPON_PRESS_IS_EXIST); | |||
| } | |||
| try { | |||
| wxOrderPressService.pressCouponJoin(order, coupon, getUserId()); | |||
| WxOrderPress orderPress = wxOrderPressService.pressCouponJoin(order, wxCouponCVo, cUserId); | |||
| if (orderPress != null) { | |||
| // 更新砍价信息 | |||
| Integer index = order.getPressCurrentNum() + 1; | |||
| if(index < wxCouponCVo.getPressLimitNum()) { | |||
| // 未完成 | |||
| order.setId(order.getId()); | |||
| order.setPressCurrentNum(index); | |||
| order.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue()); | |||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode()); | |||
| order.setUpdateDate(new Date()); | |||
| orderRedisTemplate.opsForValue().set(key, order, 3600, TimeUnit.SECONDS); | |||
| updatePressOrderPressing(order, index); | |||
| } else { | |||
| order.setPressCurrentNum(index); | |||
| order.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue()); | |||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESS_COMPLETE.getCode()); | |||
| order.setUpdateDate(curDate); | |||
| orderRedisTemplate.opsForValue().set(key, order, 3600, TimeUnit.SECONDS); | |||
| updatePressOrderComplete(order, wxCouponCVo, index); | |||
| } | |||
| } | |||
| } catch (MallinkException e) { | |||
| redisLock.unlock(orderIdStr, timeStr); | |||
| logger.error(e.getMessage()); | |||
| return new ResultData(e.getErrorCode(), e.getMessage()); | |||
| } | |||
| redisLock.unlock(orderIdStr, timeStr); | |||
| return new ResultData(); | |||
| } | |||
| private void updatePressOrderPressing(WxOrder order, Integer index) { | |||
| WxOrder orderUpdatePress = new WxOrder(); | |||
| orderUpdatePress.setId(order.getId()); | |||
| orderUpdatePress.setPressCurrentNum(index); | |||
| orderUpdatePress.setPressCurrentValue(order.getPressCurrentValue()); | |||
| orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode()); | |||
| orderUpdatePress.setUpdateDate(new Date()); | |||
| // 保存订单 | |||
| wxOrderService.saveOrUpdate(orderUpdatePress); | |||
| } | |||
| private void updatePressOrderComplete(WxOrder order, WxCouponCVo wxCouponCVo, Integer index) { | |||
| WxOrder orderUpdatePress = new WxOrder(); | |||
| orderUpdatePress.setId(order.getId()); | |||
| orderUpdatePress.setPressCurrentNum(index); | |||
| orderUpdatePress.setPressCurrentValue(order.getPressCurrentValue()); | |||
| orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESS_COMPLETE.getCode()); | |||
| orderUpdatePress.setPayment(wxCouponCVo.getSalePrice()); | |||
| orderUpdatePress.setUpdateDate(new Date()); | |||
| // 保存订单 | |||
| wxOrderService.saveOrUpdate(orderUpdatePress); | |||
| // 发送砍价成功消息 | |||
| if(index.equals(wxCouponCVo.getPressLimitNum())) { | |||
| wxOrderPressService.sendPressComplateMsg(orderUpdatePress); | |||
| } | |||
| } | |||
| @ApiOperation(value = "砍价状态(1:我发起的砍价,2:未参与的砍价, 3:已参与的砍价)", notes = "") | |||
| @GetMapping("getPressOrderStatus") | |||
| @ApiImplicitParam(name = "orderId", value = "orderId", dataType = "String", paramType = "query", required = true) | |||
| @@ -8,6 +8,7 @@ import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.iformall.annotation.AuthIgnore; | |||
| import com.iformall.annotation.RedisCache; | |||
| import com.iformall.common.ErrorCode; | |||
| import com.iformall.common.Result; | |||
| import com.iformall.common.ResultData; | |||
| @@ -633,6 +634,7 @@ public class WxUserGrantController extends BaseController { | |||
| return new ResultData(Result.SUCCESS, "是老用户,已完成手机号授权", resultMap); | |||
| } | |||
| @RedisCache | |||
| @ApiOperation("获取当前用户信息") | |||
| @GetMapping("/userinfo") | |||
| public ResultData getUserInfo() { | |||
| @@ -13,8 +13,8 @@ | |||
| <artifactId>mallinkCallback</artifactId> | |||
| <properties> | |||
| <weixin-java-mp.version>3.7.0</weixin-java-mp.version> | |||
| <weixin-java-open.version>3.7.0</weixin-java-open.version> | |||
| <weixin-java-mp.version>3.7.0.B</weixin-java-mp.version> | |||
| <weixin-java-open.version>3.7.0.B</weixin-java-open.version> | |||
| </properties> | |||
| <dependencies> | |||
| @@ -236,6 +236,7 @@ public enum ErrorCode{ | |||
| COUPON_PASSWORD_NOT_FOUND(9501, "兑换码未找到"), | |||
| COUPON_PASSWORD_HAD_USED(9502, "兑换码已使用"), | |||
| COUPON_PASSWORD_IS_CANCELED(9503, "兑换码已作废"), | |||
| COUPON_PASSWORD_POSTPONE_ERR(9504, "卡密延期异常"), | |||
| /** | |||
| * 微信 | |||
| @@ -81,7 +81,7 @@ public class WxMerchant extends TenantEntity { | |||
| } | |||
| @TableField(exist = false) | |||
| @Excel(name="账户类型",width = 20,orderNum = "7",replace = {"_null","商户号_0","微信号_1"}) | |||
| @Excel(name="账户类型",width = 20,orderNum = "7",replace = {"_null","商户号_0","微信号_1","OPENID_3"}) | |||
| @io.swagger.annotations.ApiModelProperty(value="账户类型",name="accountTypeValue") | |||
| private Integer accountTypeValue; | |||
| @@ -0,0 +1,67 @@ | |||
| package com.iformall.domain.vo; | |||
| import com.iformall.domain.po.WxOrder; | |||
| import io.swagger.annotations.ApiModelProperty; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import lombok.ToString; | |||
| @Data | |||
| @ToString(callSuper = true) | |||
| @EqualsAndHashCode(callSuper = true) | |||
| public class WxOrderPayExpVo extends WxOrder { | |||
| /** 发券商户信息 */ | |||
| @ApiModelProperty(value="商户名",name="merchantName") | |||
| private String merchantName; | |||
| @ApiModelProperty(value="b端用户姓名",name="bUserName") | |||
| private String bUserName; | |||
| @ApiModelProperty(value="b端用户手机号",name="bUserPhone") | |||
| private String bUserPhone; | |||
| /** c端用户信息 */ | |||
| @ApiModelProperty(value="c用户绑定的手机号",name="cUserPhone") | |||
| private String cUserPhone; | |||
| @ApiModelProperty(value="c用户昵称",name="nickName") | |||
| private String nickName; | |||
| @ApiModelProperty(value="c用户名",name="cUserName") | |||
| private String cUserName; | |||
| @ApiModelProperty(value ="支付订单ID", name = "payOrderId") | |||
| private Long payOrderId; | |||
| @ApiModelProperty(value ="微信支付订单ID", name = "transactionId") | |||
| private String transactionId; | |||
| public static enum Field | |||
| { | |||
| Id_ASC("`id` ASC"),Id_DESC("`id` DESC") | |||
| ,OrderNumber_ASC("`order_number` ASC"),OrderNumber_DESC("`order_number` DESC") | |||
| ,TenantId_ASC("`tenant_id` ASC"),TenantId_DESC("`tenant_id` DESC") | |||
| ,CUserId_ASC("`c_user_id` ASC"),CUserId_DESC("`c_user_id` DESC") | |||
| ,CouponId_ASC("`product_id` ASC"),CouponId_DESC("`product_id` DESC") | |||
| ,PaymentType_ASC("`payment_type` ASC"),PaymentType_DESC("`payment_type` DESC") | |||
| ,Payment_ASC("`payment` ASC"),Payment_DESC("`payment` DESC") | |||
| ,PaymentTime_ASC("`payment_time` ASC"),PaymentTime_DESC("`payment_time` DESC") | |||
| ,OrderStatus_ASC("`order_status` ASC"),OrderStatus_DESC("`order_status` DESC") | |||
| ,CreateDate_ASC("`create_date` ASC"),CreateDate_DESC("`create_date` DESC") | |||
| ,UpdateDate_ASC("`update_date` ASC"),UpdateDate_DESC("`update_date` DESC") | |||
| ,Detail_ASC("`detail` ASC"),Detail_DESC("`detail` DESC") | |||
| ; | |||
| private String value; | |||
| Field(String value){ | |||
| this.value = value; | |||
| } | |||
| public String getValue() { | |||
| return value; | |||
| } | |||
| public void setCol(String value) { | |||
| this.value = value; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return this.getValue(); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package com.iformall.domain.vo; | |||
| import com.iformall.domain.po.WxOrder; | |||
| import io.swagger.annotations.ApiModelProperty; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import lombok.ToString; | |||
| @@ -13,20 +14,19 @@ import java.util.List; | |||
| public class WxOrderPayVo extends WxOrder { | |||
| /**发券商户信息*/ | |||
| @io.swagger.annotations.ApiModelProperty(value="商户名",name="merchantName") | |||
| @ApiModelProperty(value="商户名",name="merchantName") | |||
| private String merchantName; | |||
| @io.swagger.annotations.ApiModelProperty(value="b端用户姓名",name="bUserName") | |||
| @ApiModelProperty(value="b端用户姓名",name="bUserName") | |||
| private String bUserName; | |||
| @io.swagger.annotations.ApiModelProperty(value="b端用户手机号",name="bUserPhone") | |||
| @ApiModelProperty(value="b端用户手机号",name="bUserPhone") | |||
| private String bUserPhone; | |||
| @io.swagger.annotations.ApiModelProperty(value="c用户绑定的手机号",name="cUserPhone") | |||
| @ApiModelProperty(value="c用户绑定的手机号",name="cUserPhone") | |||
| private String cUserPhone; | |||
| @io.swagger.annotations.ApiModelProperty(value="c用户昵称",name="nickName") | |||
| @ApiModelProperty(value="c用户昵称",name="nickName") | |||
| private String nickName; | |||
| @io.swagger.annotations.ApiModelProperty(value="c用户名",name="cUserName") | |||
| @ApiModelProperty(value="c用户名",name="cUserName") | |||
| private String cUserName; | |||
| public static enum Field | |||
| @@ -21,4 +21,6 @@ public interface WxCouponPasswordMapper extends CommonMapper<WxCouponPassword, L | |||
| void updateStatus(List<WxCouponPassword> tempList); | |||
| void updatePostpone(WxCouponPassword wxCouponPassword); | |||
| } | |||
| @@ -24,8 +24,10 @@ public interface WxOrderMapper extends CommonMapper<WxOrder, Long> { | |||
| List<WxOrderCouponVo> findListOfCUser(WxOrder wxOrder); | |||
| List<WxOrderCouponVo> selectDetailOfCUser(WxOrder wxOrder); | |||
| /** B端收银台(收款码支付) */ | |||
| Map dayMicroPayAmountList(Map dateMap); | |||
| List<WxOrderPayVo> findListOfMicroPayOrderByDate(Map dateMap); | |||
| List<WxOrderPayExpVo> findListOfExpMicroPayOrderByDate(Map dateMap); | |||
| List<MarkingSceneDataVo> queryForSceneRepotyHistory(HashMap<String,Object> params); | |||
| @@ -12,4 +12,6 @@ public interface WxPayOrderMapper extends CommonMapper<WxPayOrder, Long> { | |||
| List<WxPayOrderVo> findListOfPaidOrderByDate(Map dateMap); | |||
| List<WxPayOrder> findListForUnPs(WxPayOrder wxPayOrder); | |||
| List<WxPayOrder> findExpListForMicroPay(WxPayOrder wxPayOrder); | |||
| } | |||
| @@ -11,6 +11,7 @@ public class WxPayOrderQ { | |||
| private String mch_id; // 商户号 | |||
| private String nonce_str; // 随机字符串 | |||
| private String sign; // 签名 | |||
| private String transaction_id; // 微信订单号 | |||
| private String out_trade_no; // 商户订单号 | |||
| @Override | |||
| @@ -14,6 +14,7 @@ public class WxPayOrderSQ { | |||
| private String nonce_str; // 随机字符串 | |||
| private String sign; // 签名 | |||
| private String sign_type; // 签名类型 | |||
| private String transaction_id; // 微信订单号 | |||
| private String out_trade_no; // 商户订单号 | |||
| @Override | |||
| @@ -26,6 +27,7 @@ public class WxPayOrderSQ { | |||
| sb.append(", nonce_str='").append(nonce_str).append('\''); | |||
| sb.append(", sign='").append(sign).append('\''); | |||
| sb.append(", out_trade_no='").append(out_trade_no).append('\''); | |||
| sb.append(", transtion_id='").append(out_trade_no).append('\''); | |||
| sb.append('}'); | |||
| return sb.toString(); | |||
| } | |||
| @@ -76,6 +76,7 @@ public class ExcelService { | |||
| response.getOutputStream().write(new byte[] { (byte) 0xEF, (byte) 0xBB,(byte) 0xBF }); // csv 乱码问题 | |||
| CsvExportUtil.exportCsv(exportParams, pojoClass, list, response.getOutputStream()); | |||
| } catch (IOException e) { | |||
| logger.error("defaultExportCsv error ",e); | |||
| throw new MallinkException(ErrorCode.EXCEL_EXPORT_ERROR.getCode(), e.getMessage()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.iformall.service; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| import java.util.Set; | |||
| public interface RedisService { | |||
| String KEY_HOT_API = "hotapi" ; | |||
| /** | |||
| * 获取热点缓存接口列表 | |||
| * @return | |||
| */ | |||
| Collection<Object> hotApi() ; | |||
| /** | |||
| * 清除指定key | |||
| * @param keys | |||
| * @return | |||
| */ | |||
| boolean delRedisKey(Collection<Object> keys) ; | |||
| } | |||
| @@ -70,4 +70,6 @@ public interface WxCouponPasswordService { | |||
| PageInfo<WxCouponPasswordCountInfoVO> findCouponList(WxCouponPassword wxCouponPassword, Integer pageNum, Integer pageSize); | |||
| List<WxCouponPassword> findList(WxCouponPassword wxCouponPassword); | |||
| void postpone(WxCouponPassword wxCouponPassword); | |||
| } | |||
| @@ -47,7 +47,7 @@ public interface WxOrderPressService { | |||
| * @param coupon | |||
| * @param cUserId | |||
| */ | |||
| void pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId); | |||
| WxOrderPress pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId); | |||
| /** | |||
| * 检查用户砍价状态 | |||
| @@ -55,6 +55,10 @@ public interface WxOrderPressService { | |||
| * @param cUserId | |||
| */ | |||
| int pressCouponStatus(WxOrder order, Long cUserId); | |||
| boolean checkCUserHasJoin(WxOrder order, Long cUserId); | |||
| void sendPressComplateMsg(WxOrder orderUpdatePress); | |||
| @@ -37,6 +37,14 @@ public interface WxOrderService { | |||
| ResultData microPayListOnDateAsPage(Long bUserId, Integer type, String date, Integer pageIndex, Integer pageSize); | |||
| /** | |||
| * 根据日期 b用户查刷卡异常支付list | |||
| * | |||
| * @param bUserId B端用户) | |||
| */ | |||
| ResultData microPayExpListOnDateAsPage(Long bUserId, Integer type, String date, Integer pageIndex, Integer pageSize); | |||
| /** | |||
| * 付款码支付 | |||
| * @param user | |||
| @@ -106,6 +114,14 @@ public interface WxOrderService { | |||
| * @return | |||
| */ | |||
| WxOrder getById(Long id); | |||
| /** | |||
| * 根据Id获得实体 | |||
| * | |||
| * @param id | |||
| * @return | |||
| */ | |||
| WxOrder getPressOrderById(Long id); | |||
| /** | |||
| * 保存或更新实体 | |||
| @@ -0,0 +1,46 @@ | |||
| package com.iformall.service.impl; | |||
| import com.iformall.service.RedisService; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.commons.collections.CollectionUtils; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.dao.DataAccessException; | |||
| import org.springframework.data.redis.core.RedisOperations; | |||
| import org.springframework.data.redis.core.SessionCallback; | |||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||
| import org.springframework.stereotype.Service; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| import java.util.Set; | |||
| @Slf4j | |||
| @Service | |||
| public class RedisServiceImpl implements RedisService { | |||
| @Autowired | |||
| private StringRedisTemplate stringRedisTemplate; | |||
| @Override | |||
| public Collection<Object> hotApi() { | |||
| return stringRedisTemplate.opsForHash().keys(KEY_HOT_API); | |||
| } | |||
| @Override | |||
| public boolean delRedisKey(Collection<Object> keys) { | |||
| if (CollectionUtils.isEmpty(keys)) { | |||
| return false; | |||
| } | |||
| boolean result = stringRedisTemplate.execute(new SessionCallback<Boolean>() { | |||
| @Override | |||
| public Boolean execute(RedisOperations redisOperations) throws DataAccessException { | |||
| redisOperations.multi(); | |||
| redisOperations.opsForValue().getOperations().delete(keys); | |||
| redisOperations.opsForHash().delete(KEY_HOT_API, keys.toArray()); | |||
| redisOperations.exec(); | |||
| return true ; | |||
| } | |||
| }); | |||
| return result; | |||
| } | |||
| } | |||
| @@ -167,6 +167,11 @@ public class WxCouponPasswordServiceImpl implements WxCouponPasswordService { | |||
| return wxCouponPasswordMapper.findList(wxCouponPassword); | |||
| } | |||
| @Override | |||
| public void postpone(WxCouponPassword wxCouponPassword) { | |||
| } | |||
| /* | |||
| public static void main(String[] args) { | |||
| WxCouponPasswordServiceImpl ll = new WxCouponPasswordServiceImpl(); | |||
| @@ -24,11 +24,14 @@ import org.apache.commons.lang3.StringUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Qualifier; | |||
| import org.springframework.data.redis.core.RedisTemplate; | |||
| import org.springframework.stereotype.Service; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Calendar; | |||
| import java.util.Date; | |||
| import java.util.concurrent.TimeUnit; | |||
| @Service | |||
| public class WxOrderPressServiceImpl implements WxOrderPressService { | |||
| @@ -52,7 +55,6 @@ public class WxOrderPressServiceImpl implements WxOrderPressService { | |||
| @Autowired | |||
| MqBaseProducer mqBaseProducer; | |||
| @Override | |||
| public PageInfo<WxOrderPress> listAsPage(WxOrderPress record, Integer pageIndex, Integer pageSize) { | |||
| return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxOrderPressMapper.findList(record)); | |||
| @@ -82,25 +84,12 @@ public class WxOrderPressServiceImpl implements WxOrderPressService { | |||
| @Override | |||
| public void pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId) { | |||
| public WxOrderPress pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId) { | |||
| final IdWorker idWorker = IdWorker.get(); | |||
| Date curDate = new Date(); | |||
| // 检查此人是否已参与砍价 | |||
| WxOrderPress orderPressQ = new WxOrderPress(); | |||
| orderPressQ.setTenantId(order.getTenantId()); | |||
| orderPressQ.setOrderId(order.getId()); | |||
| orderPressQ.setUserId(cUserId); | |||
| QueryWrapper<WxOrderPress> queryWrapper = new QueryWrapper<>(); | |||
| queryWrapper.setEntity(orderPressQ); | |||
| int count = wxOrderPressMapper.selectCount(queryWrapper); | |||
| if(count >= 1) { | |||
| logger.error("用户已参与砍价: " + cUserId); | |||
| throw new MallinkException(ErrorCode.COUPON_PRESS_IS_EXIST); | |||
| } | |||
| // 添加 wx_order_press | |||
| int total = coupon.getPrice() - coupon.getSalePrice(); | |||
| int left_total = order.getPressCurrentValue(); | |||
| WxOrderPress orderPress = new WxOrderPress(); | |||
| orderPress.setId(idWorker.nextId()); | |||
| orderPress.setTenantId(order.getTenantId()); | |||
| @@ -109,7 +98,7 @@ public class WxOrderPressServiceImpl implements WxOrderPressService { | |||
| orderPress.setUserId(cUserId); | |||
| orderPress.setCreateDate(curDate); | |||
| orderPress.setFirst(EnumOrderPressType.NORMAL.getCode()); | |||
| orderPress.setPressValue(PressUtils.stateLessPressValue(total, left_total, coupon.getPressLimitNum(), order.getPressCurrentNum())); | |||
| orderPress.setPressValue(PressUtils.stateLessPressValue(total, order.getPressCurrentValue(), coupon.getPressLimitNum(), order.getPressCurrentNum())); | |||
| try { | |||
| // 保存订单 | |||
| @@ -118,30 +107,20 @@ public class WxOrderPressServiceImpl implements WxOrderPressService { | |||
| logger.error("保存砍价记录Error:" + e.getMessage()); | |||
| throw new MallinkException(ErrorCode.DB_FAIL); | |||
| } | |||
| return orderPress; | |||
| } | |||
| // 更新砍价信息 | |||
| Integer index = order.getPressCurrentNum() + 1; | |||
| WxOrder orderUpdatePress = new WxOrder(); | |||
| orderUpdatePress.setId(order.getId()); | |||
| orderUpdatePress.setPressCurrentNum(index); | |||
| orderUpdatePress.setPressCurrentValue(left_total - orderPress.getPressValue()); | |||
| if(index.equals(coupon.getPressLimitNum())) { | |||
| orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESS_COMPLETE.getCode()); | |||
| orderUpdatePress.setPayment(coupon.getSalePrice()); | |||
| @Override | |||
| public boolean checkCUserHasJoin(WxOrder order, Long cUserId) { | |||
| WxOrderPress orderPressQ = new WxOrderPress(); | |||
| orderPressQ.setTenantId(order.getTenantId()); | |||
| orderPressQ.setOrderId(order.getId()); | |||
| orderPressQ.setUserId(cUserId); | |||
| int count = wxOrderPressMapper.selectCount(new QueryWrapper<>(orderPressQ)); | |||
| if(count >= 1) { | |||
| return true; | |||
| } else { | |||
| orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode()); | |||
| } | |||
| orderUpdatePress.setUpdateDate(new Date()); | |||
| try { | |||
| // 保存订单 | |||
| wxOrderMapper.updateById(orderUpdatePress); | |||
| } catch (RuntimeException e) { | |||
| logger.error("更新订单Error:" + e.getMessage()); | |||
| throw new MallinkException(ErrorCode.DB_FAIL); | |||
| } | |||
| // 发送砍价成功消息 | |||
| if(index.equals(coupon.getPressLimitNum())) { | |||
| sendPressComplateMsg(orderUpdatePress); | |||
| return false; | |||
| } | |||
| } | |||
| @@ -167,7 +146,8 @@ public class WxOrderPressServiceImpl implements WxOrderPressService { | |||
| * | |||
| * @param | |||
| */ | |||
| private void sendPressComplateMsg(WxOrder orderUpdatePress) { | |||
| @Override | |||
| public void sendPressComplateMsg(WxOrder orderUpdatePress) { | |||
| // 1. get order info | |||
| WxOrder order = wxOrderMapper.selectById(orderUpdatePress.getId()); | |||
| if (order == null) { | |||
| @@ -201,6 +201,68 @@ public class WxOrderServiceImpl implements WxOrderService { | |||
| return new ResultData(resultMap); | |||
| } | |||
| @Override | |||
| public ResultData microPayExpListOnDateAsPage(Long bUserId, Integer type, String date, Integer pageIndex, Integer pageSize) { | |||
| WxMerchantBUser wxMerchantBUser = wxMerchantBUserMapper.selectById(bUserId.longValue()); | |||
| if (wxMerchantBUser == null) { | |||
| logger.error("B端用户不存在:" + bUserId); | |||
| throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND); | |||
| } | |||
| WxMerchant wxMerchant = wxMerchantMapper.selectById(wxMerchantBUser.getMerchantId()); | |||
| if (wxMerchant == null) { | |||
| logger.error("商户不存在:" + bUserId); | |||
| throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND); | |||
| } | |||
| SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); | |||
| String dateString; | |||
| Date startDate; | |||
| Date endDate; | |||
| if (date == null) { | |||
| endDate = new Date(); | |||
| dateString = fmt.format(new Date()); | |||
| try { | |||
| startDate = fmt.parse(dateString); | |||
| } catch (ParseException e) { | |||
| return new ResultData(ErrorCode.SYS_PARAMETER_CAST_ERROR); | |||
| } | |||
| } else { | |||
| try { | |||
| startDate = fmt.parse(date); | |||
| } catch (ParseException e) { | |||
| return new ResultData(ErrorCode.SYS_PARAMETER_CAST_ERROR); | |||
| } | |||
| Calendar cal = Calendar.getInstance(); | |||
| cal.setTime(startDate); | |||
| cal.add(Calendar.DATE, 1); | |||
| endDate = cal.getTime(); | |||
| } | |||
| Map dateMap = new HashMap(); | |||
| dateMap.put("startDate", startDate); | |||
| dateMap.put("endDate", endDate); | |||
| dateMap.put("merchantId", wxMerchant.getId()); | |||
| dateMap.put("type", type); | |||
| Map resultMap = new HashMap(); | |||
| if (pageIndex == null || pageSize == null) { | |||
| List<WxOrderPayExpVo> list; | |||
| list = wxOrderMapper.findListOfExpMicroPayOrderByDate(dateMap); | |||
| logger.info("find " + list.size() + " order from " + startDate + " to " + new Date()); | |||
| int total_price = 0; | |||
| for (WxOrder order : list) { | |||
| total_price = total_price + order.getPayment(); | |||
| } | |||
| resultMap.put("amount", total_price); | |||
| } else { | |||
| resultMap.put("list", PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxOrderMapper.findListOfExpMicroPayOrderByDate(dateMap))); | |||
| } | |||
| return new ResultData(resultMap); | |||
| } | |||
| /** | |||
| * 拼团数量 | |||
| * | |||
| @@ -1365,6 +1427,12 @@ public class WxOrderServiceImpl implements WxOrderService { | |||
| return wxOrderMapper.selectById(id); | |||
| } | |||
| @Override | |||
| public WxOrder getPressOrderById(Long id) { | |||
| return wxOrderMapper.selectById(id); | |||
| } | |||
| @Override | |||
| public void saveOrUpdate(WxOrder record) { | |||
| if (record.getId() == null) { | |||
| @@ -646,7 +646,11 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||
| payOrderQ.setAppid(appInfo.getAppId()); | |||
| payOrderQ.setMch_id(payAccount.getMchId()); | |||
| payOrderQ.setNonce_str(noncestr); | |||
| payOrderQ.setOut_trade_no(record.getPayOrderNo()); | |||
| if (StringUtils.isNotBlank(record.getTransactionId())) { | |||
| payOrderQ.setTransaction_id(record.getTransactionId()); | |||
| } else { | |||
| payOrderQ.setOut_trade_no(record.getPayOrderNo()); | |||
| } | |||
| try { | |||
| Map map = BeanUtils.toStringMap(payOrderQ); | |||
| @@ -668,7 +672,11 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||
| payOrderSQ.setMch_id(payAccount.getMchId()); | |||
| payOrderSQ.setSub_mch_id(payAccount.getSubMchId()); | |||
| payOrderSQ.setNonce_str(noncestr); | |||
| payOrderSQ.setOut_trade_no(record.getPayOrderNo()); | |||
| if (StringUtils.isNotBlank(record.getTransactionId())) { | |||
| payOrderSQ.setTransaction_id(record.getTransactionId()); | |||
| } else { | |||
| payOrderSQ.setOut_trade_no(record.getPayOrderNo()); | |||
| } | |||
| payOrderSQ.setSign_type("HMAC-SHA256"); | |||
| try { | |||
| @@ -764,14 +772,24 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR, returnMap); | |||
| } | |||
| } else { | |||
| String errMsg = ""; | |||
| JSONObject errObj = errorMapQuery.getJSONObject(result_code); | |||
| if (errObj != null) { | |||
| errMsg = errObj.toJSONString(); | |||
| String errCode = returnMap.get("err_code"); | |||
| String errCodeDes = returnMap.get("err_code_des"); | |||
| if (errCode.equalsIgnoreCase("ORDERNOTEXIST")) { | |||
| // 支付订单不存在, 订单失败 | |||
| WxOrder updateOrderForMicroPay = new WxOrder(); | |||
| updateOrderForMicroPay.setId(order.getId()); | |||
| updateOrderForMicroPay.setOrderStatus(EnumOrderStatus.ORDER_STATUS_OVERTIME_CANCEL.getCode()); | |||
| try { | |||
| wxOrderMapper.updateById(updateOrderForMicroPay); | |||
| } catch (Exception e) { | |||
| logger.error(e.getMessage()); | |||
| } | |||
| } | |||
| String errMsg = returnMap.get("return_msg"); | |||
| if (StringUtils.isNotBlank(errMsg)) { | |||
| record.setFailReason(errMsg); | |||
| } else { | |||
| errMsg = returnMap.get("return_msg"); | |||
| record.setFailReason(errMsg); | |||
| record.setFailReason(errCodeDes); | |||
| } | |||
| record.setUpdateTime(new Date()); | |||
| try { | |||
| @@ -783,8 +801,25 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap); | |||
| } | |||
| } else { | |||
| String errCode = returnMap.get("err_code"); | |||
| String errCodeDes = returnMap.get("err_code_des"); | |||
| if (errCode.equalsIgnoreCase("ORDERNOTEXIST")) { | |||
| // 支付订单不存在, 订单失败 | |||
| WxOrder updateOrderForMicroPay = new WxOrder(); | |||
| updateOrderForMicroPay.setId(order.getId()); | |||
| updateOrderForMicroPay.setOrderStatus(EnumOrderStatus.ORDER_STATUS_OVERTIME_CANCEL.getCode()); | |||
| try { | |||
| wxOrderMapper.updateById(updateOrderForMicroPay); | |||
| } catch (Exception e) { | |||
| logger.error(e.getMessage()); | |||
| } | |||
| } | |||
| String errMsg = returnMap.get("return_msg"); | |||
| record.setFailReason(errMsg); | |||
| if (StringUtils.isNotBlank(errMsg)) { | |||
| record.setFailReason(errMsg); | |||
| } else { | |||
| record.setFailReason(errCodeDes); | |||
| } | |||
| record.setUpdateTime(new Date()); | |||
| try { | |||
| wxPayOrderMapper.updateById(record); | |||
| @@ -55,6 +55,37 @@ public class RedisLock { | |||
| return false; | |||
| } | |||
| /** | |||
| * 加锁 ,设置过期时间 | |||
| * @param key productId - 商品的唯一标志 | |||
| * @param value 当前时间+超时时间 | |||
| * @return | |||
| */ | |||
| public boolean lock2(String key,String value){ | |||
| ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); | |||
| //对应setnx命令,5s过期 | |||
| if(operations.setIfAbsent(key,value,5,TimeUnit.SECONDS)){ | |||
| //可以成功设置,也就是key不存在 | |||
| return true; | |||
| } | |||
| //判断锁超时 - 防止原来的操作异常,没有运行解锁操作 防止死锁 | |||
| String currentValue = operations.get(key); | |||
| //如果锁过期 | |||
| if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){//currentValue不为空且小于当前时间 | |||
| //获取上一个锁的时间value | |||
| String oldValue =operations.getAndSet(key,value);//对应getset,如果key存在 | |||
| //假设两个线程同时进来,key被占用了。获取的值currentValue=A(get取的旧的值肯定是一样的),两个线程的value都是B,key都是K.锁时间已经过期了。 | |||
| //而这里面的getAndSet一次只会一个执行,也就是一个执行之后,上一个的value已经变成了B。只有一个线程获取的上一个值会是A,另一个线程拿到的值是B。 | |||
| if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue) ){ | |||
| //oldValue不为空且oldValue等于currentValue,也就是校验是不是上个对应的商品时间戳,也是防止并发 | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * 解锁 | |||
| @@ -159,4 +159,10 @@ | |||
| </foreach> | |||
| </update> | |||
| <update id="updatePostpone"> | |||
| update wx_coupon_password | |||
| set expire_date=#{expireDate} | |||
| where tenant_id=#{tenantId} and coupon_id=#{couponId} and status=1 | |||
| </update> | |||
| </mapper> | |||
| @@ -430,6 +430,69 @@ | |||
| order by o.create_date desc | |||
| </select> | |||
| <resultMap id="PayExpVoResultMap" type="com.iformall.domain.vo.WxOrderPayExpVo"> | |||
| <id column="id" jdbcType="BIGINT" property="id"/> | |||
| <result column="order_number" jdbcType="BIGINT" property="orderNumber"/> | |||
| <result column="tenant_id" jdbcType="VARCHAR" property="tenantId"/> | |||
| <result column="c_user_id" jdbcType="BIGINT" property="cUserId"/> | |||
| <result column="coupon_channel_id" jdbcType="BIGINT" property="couponChannelId"/> | |||
| <result column="product_id" jdbcType="BIGINT" property="productId"/> | |||
| <result column="type" jdbcType="TINYINT" property="type"/> | |||
| <result column="payment_type" jdbcType="INTEGER" property="paymentType"/> | |||
| <result column="payment" jdbcType="INTEGER" property="payment"/> | |||
| <result column="payment_time" jdbcType="TIMESTAMP" property="paymentTime"/> | |||
| <result column="order_status" jdbcType="INTEGER" property="orderStatus"/> | |||
| <result column="create_date" jdbcType="TIMESTAMP" property="createDate"/> | |||
| <result column="update_date" jdbcType="TIMESTAMP" property="updateDate"/> | |||
| <result column="detail" jdbcType="VARCHAR" property="detail"/> | |||
| <result column="merchant_name" jdbcType="VARCHAR" property="merchantName" /> | |||
| <result column="name" jdbcType="VARCHAR" property="bUserName" /> | |||
| <result column="bphone" jdbcType="VARCHAR" property="bUserPhone" /> | |||
| <result column="phone" jdbcType="VARCHAR" property="cUserPhone" /> | |||
| <result column="nick_name" jdbcType="VARCHAR" property="nickName" /> | |||
| <result column="c_user_name" jdbcType="VARCHAR" property="cUserName" /> | |||
| <result column="pay_order_id" jdbcType="BIGINT" property="payOrderId"/> | |||
| <result column="transaction_id" jdbcType="VARCHAR" property="transactionId"/> | |||
| </resultMap> | |||
| <sql id="allExpPayOrderColumns"> | |||
| o.`id`,o.`order_number`,o.`tenant_id`,o.`c_user_id`,o.`coupon_channel_id`,o.`product_id`,o.`type`, | |||
| o.`payment_type`,o.`payment`,o.`payment_time`,o.`order_status`,o.`create_date`,o.`update_date`,o.`detail`, | |||
| m.name as merchant_name, bu.name, bu.phone as bphone, | |||
| cu.phone, cu.nick_name, cubi.name c_user_name, | |||
| po.id as pay_order_id, po.transaction_id | |||
| </sql> | |||
| <select id="findListOfExpMicroPayOrderByDate" parameterType="map" resultMap="PayExpVoResultMap"> | |||
| select | |||
| <include refid="allExpPayOrderColumns"/> | |||
| from wx_order o | |||
| inner join wx_merchant_b_user bu on bu.id = o.product_id | |||
| inner join wx_merchant m on m.id = bu.merchant_id | |||
| left join wx_pay_order po on po.order_id = o.id | |||
| LEFT JOIN wx_c_user cu on cu.id = o.c_user_id | |||
| LEFT JOIN wx_c_user_basic_info cubi on cubi.id = o.c_user_id | |||
| where o.order_status = '0' and o.`type` = 1 | |||
| <if test=" null != tenantId "> | |||
| and o.`tenant_id` = #{tenantId} | |||
| </if> | |||
| <if test=" null != merchantId "> | |||
| and bu.`merchant_id` = #{merchantId} | |||
| </if> | |||
| <if test=" null != bUserId "> | |||
| and bu.`id` = #{bUserId} | |||
| </if> | |||
| <if test="startDate != null"> | |||
| AND o.create_date > #{startDate,jdbcType=TIMESTAMP} | |||
| </if> | |||
| <if test="endDate != null"> | |||
| AND o.create_date < #{endDate,jdbcType=TIMESTAMP} | |||
| </if> | |||
| order by o.create_date desc | |||
| </select> | |||
| <select id="queryForSceneRepotyHistory" resultType="com.iformall.domain.vo.MarkingSceneDataVo" parameterType="hashmap"> | |||
| select DATE_FORMAT(o.create_date,'%Y-%m-%d') xTime, count(o.id) triggerCount | |||
| from wx_order o | |||
| @@ -159,6 +159,17 @@ | |||
| and tenant_id in (select tenant_id from wx_pay_account where share =1) and tenant_id != '789' and tenant_id != '1000' ; | |||
| </select> | |||
| <select id="findExpListForMicroPay" parameterType="com.iformall.domain.po.WxPayOrder" resultMap="BaseResultMap"> | |||
| select | |||
| <include refid="allColumns"/> | |||
| from wx_pay_order | |||
| where 1=1 | |||
| <if test=" null != tenantId "> and `tenant_id` = #{tenantId} </if> | |||
| and order_id in ( | |||
| select id from wx_order where `tenant_id` = #{tenantId} and type = 1 and order_status = 0 | |||
| ); | |||
| </select> | |||
| <resultMap id="VBaseResultMap" type="com.iformall.domain.vo.WxPayOrderVo"> | |||
| <id column="id" jdbcType="BIGINT" property="id"/> | |||
| <result column="tenant_id" jdbcType="VARCHAR" property="tenantId"/> | |||
| @@ -13,8 +13,8 @@ | |||
| <artifactId>mallinkSysAdmin</artifactId> | |||
| <properties> | |||
| <weixin-java-mp.version>3.7.0</weixin-java-mp.version> | |||
| <weixin-java-open.version>3.7.0</weixin-java-open.version> | |||
| <weixin-java-mp.version>3.7.0.B</weixin-java-mp.version> | |||
| <weixin-java-open.version>3.7.0.B</weixin-java-open.version> | |||
| </properties> | |||
| <dependencies> | |||
| @@ -0,0 +1,91 @@ | |||
| package com.iformall.controller.micropay; | |||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||
| import com.iformall.common.ResultData; | |||
| import com.iformall.controller.base.BaseController; | |||
| import com.iformall.domain.po.WxMerchantBUser; | |||
| import com.iformall.domain.po.WxOrder; | |||
| import com.iformall.domain.po.WxPayOrder; | |||
| import com.iformall.enums.EnumOrderStatus; | |||
| import com.iformall.exception.MallinkException; | |||
| import com.iformall.mapper.WxMerchantBUserMapper; | |||
| import com.iformall.mapper.WxOrderMapper; | |||
| import com.iformall.mapper.WxPayOrderMapper; | |||
| import com.iformall.service.WxPayOrderService; | |||
| import io.swagger.annotations.ApiOperation; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import java.util.List; | |||
| /** | |||
| * 数据初始化 | |||
| */ | |||
| @Slf4j | |||
| @RestController | |||
| @RequestMapping("micropay") | |||
| public class MicropayController extends BaseController { | |||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||
| @Autowired | |||
| WxOrderMapper orderMapper; | |||
| @Autowired | |||
| WxPayOrderMapper payOrderMapper; | |||
| @Autowired | |||
| WxMerchantBUserMapper merchantBUserMapper; | |||
| @Autowired | |||
| WxPayOrderService payOrderService; | |||
| @ApiOperation(value = "异常支付查询") | |||
| @PostMapping("/checkExp") | |||
| public void check() { | |||
| logger.info("queryMicopayOrder"); | |||
| String tenantId = "1021"; | |||
| WxOrder oq = new WxOrder(); | |||
| oq.setTenantId(tenantId); | |||
| oq.setType(1); | |||
| oq.setOrderStatus(0); | |||
| List<WxOrder> orders = orderMapper.findList(oq); | |||
| log.info("EXP order num: {}", orders.size()); | |||
| for (WxOrder order: orders) { | |||
| WxMerchantBUser bUser = merchantBUserMapper.selectById(order.getProductId()); | |||
| WxPayOrder q = new WxPayOrder(); | |||
| q.setTenantId(tenantId); | |||
| q.setOrderId(order.getId()); | |||
| List<WxPayOrder> payOrderList = payOrderMapper.selectList(new QueryWrapper<>(q)); | |||
| if (payOrderList.size() > 0) { | |||
| // 查询支付订单状态 | |||
| for (WxPayOrder payOrder : payOrderList) { | |||
| try { | |||
| ResultData resultData = payOrderService.payOrderQuery(bUser, payOrder); | |||
| logger.info(resultData.toString()); | |||
| } catch (MallinkException e) { | |||
| logger.error("payment wechat, order query error, req 2: " + payOrder.toString() + ", e:" + e.getMessage()); | |||
| } catch (Exception e) { | |||
| logger.error("payment wechat, order query error, req 3: " + payOrder.toString() + ", e:" + e.getMessage()); | |||
| } | |||
| } | |||
| } else { | |||
| // 无支付订单, 直接标记订单已取消 | |||
| WxOrder updateOrderForMicroPay = new WxOrder(); | |||
| updateOrderForMicroPay.setId(order.getId()); | |||
| updateOrderForMicroPay.setOrderStatus(EnumOrderStatus.ORDER_STATUS_OVERTIME_CANCEL.getCode()); | |||
| try { | |||
| orderMapper.updateById(updateOrderForMicroPay); | |||
| } catch (Exception e) { | |||
| logger.error("update Order: {} ", e.getMessage()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -32,11 +32,11 @@ | |||
| </parent> | |||
| <properties> | |||
| <weixin-java-common.version>3.7.0</weixin-java-common.version> | |||
| <weixin-java-mp.version>3.7.0</weixin-java-mp.version> | |||
| <weixin-java-miniapp.version>3.7.0</weixin-java-miniapp.version> | |||
| <weixin-java-pay.version>3.7.0</weixin-java-pay.version> | |||
| <weixin-java-open.version>3.7.0</weixin-java-open.version> | |||
| <weixin-java-common.version>3.7.0.B</weixin-java-common.version> | |||
| <weixin-java-mp.version>3.7.0.B</weixin-java-mp.version> | |||
| <weixin-java-miniapp.version>3.7.0.B</weixin-java-miniapp.version> | |||
| <weixin-java-pay.version>3.7.0.B</weixin-java-pay.version> | |||
| <weixin-java-open.version>3.7.0.B</weixin-java-open.version> | |||
| <quartz.version>2.3.0</quartz.version> | |||
| </properties> | |||