作者 朱兆平

重复提交判定

  1 +package com.tianbo.analysis.annotation;
  2 +
  3 +
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.tianbo.analysis.controller.bean.ResponseReason;
  6 +import com.tianbo.analysis.model.ResultJson;
  7 +import com.tianbo.analysis.tools.ResubmitLock;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.aspectj.lang.ProceedingJoinPoint;
  10 +import org.aspectj.lang.annotation.Around;
  11 +import org.aspectj.lang.annotation.Aspect;
  12 +import org.aspectj.lang.reflect.MethodSignature;
  13 +import org.springframework.stereotype.Component;
  14 +
  15 +import java.lang.reflect.Method;
  16 +
  17 +@Slf4j
  18 +@Aspect
  19 +@Component
  20 +public class ReSubmitAspect {
  21 +
  22 + private final static String DATA = "data";
  23 + private final static Object PRESENT = new Object();
  24 +
  25 + @Around("@annotation(com.tianbo.analysis.annotation.ReSubmitCheck)")
  26 + public Object handleResubmit(ProceedingJoinPoint joinPoint) throws Throwable {
  27 + Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
  28 + //获取注解信息
  29 + ReSubmitCheck annotation = method.getAnnotation(ReSubmitCheck.class);
  30 + int delaySeconds = annotation.delaySeconds();
  31 + Object[] pointArgs = joinPoint.getArgs();
  32 + String key = "";
  33 + //获取第一个参数
  34 + Object firstParam = pointArgs[0];
  35 + //解析参数
  36 + if (firstParam != null) {
  37 + //生成加密参数 使用了content_MD5的加密方式
  38 + key = ResubmitLock.handleKey(JSONObject.toJSONString(firstParam));
  39 + }
  40 +
  41 + //执行锁
  42 + boolean lock = false;
  43 + try {
  44 + //设置解锁key
  45 + lock = ResubmitLock.getInstance().lock(key, PRESENT);
  46 + if (lock) {
  47 + //放行
  48 + return joinPoint.proceed();
  49 + } else {
  50 + //响应重复提交异常
  51 + return new ResultJson(ResponseReason.REAPET_SUBMIT_ERR);
  52 + }
  53 + } finally {
  54 + //设置解锁key和解锁时间
  55 + ResubmitLock.getInstance().unLock(lock, key, delaySeconds);
  56 + }
  57 + }
  58 +}
  1 +package com.tianbo.analysis.annotation;
  2 +
  3 +
  4 +import java.lang.annotation.*;
  5 +
  6 +/**
  7 + * @author mrz
  8 + */
  9 +@Target(ElementType.METHOD)
  10 +@Retention(RetentionPolicy.RUNTIME)
  11 +@Documented
  12 +public @interface ReSubmitCheck {
  13 + /**
  14 + * 延时时间 在延时多久后可以再次提交
  15 + *
  16 + * @return Time unit is one second
  17 + */
  18 + int delaySeconds() default 5;
  19 +}
  1 +package com.tianbo.analysis.controller;
  2 +
  3 +import com.tianbo.analysis.annotation.ReSubmitCheck;
  4 +import com.tianbo.analysis.annotation.UserPermissionCheck;
  5 +import com.tianbo.analysis.dao.SENDPLANMapper;
  6 +import com.tianbo.analysis.model.ResultJson;
  7 +import com.tianbo.analysis.model.SENDPLAN;
  8 +import org.springframework.web.bind.annotation.*;
  9 +
  10 +import javax.annotation.Resource;
  11 +import java.util.List;
  12 +
  13 +@RestController
  14 +@RequestMapping("/sendplan")
  15 +public class SendPlanController {
  16 +
  17 + @Resource
  18 + SENDPLANMapper sendplanMapper;
  19 +
  20 + @UserPermissionCheck
  21 + @PostMapping("add")
  22 + public ResultJson add(@RequestBody SENDPLAN sendplan,@CookieValue("username") String username,@CookieValue("userid") String userid){
  23 + return sendplanMapper.insertSelective(sendplan)>0? new ResultJson("200","新增成功") : new ResultJson("400","新增失败");
  24 + }
  25 +
  26 + @UserPermissionCheck
  27 + @PostMapping("del")
  28 + public ResultJson del(@RequestBody SENDPLAN sendplan,@CookieValue("username") String username,@CookieValue("userid") String userid){
  29 + return sendplanMapper.deleteByPrimaryKey(sendplan.getCarrier())>0? new ResultJson("200","新增成功") : new ResultJson("400","新增失败");
  30 + }
  31 +
  32 + @ReSubmitCheck
  33 + @GetMapping("list")
  34 + public ResultJson<List<SENDPLAN>> list(@RequestParam(value = "carrier",required = false) String carrier){
  35 + List result = sendplanMapper.list(carrier);
  36 + return new ResultJson("200","查询成功",result);
  37 + }
  38 +}
@@ -6,6 +6,7 @@ public enum ResponseReason { @@ -6,6 +6,7 @@ public enum ResponseReason {
6 MT8204_EDIT_ERR("8204401","直接改配申请更新失败"), 6 MT8204_EDIT_ERR("8204401","直接改配申请更新失败"),
7 MT8204_DEL_ERR("8204402","直接改配申请更新失败"), 7 MT8204_DEL_ERR("8204402","直接改配申请更新失败"),
8 MT8204_SEND_ERR("8204404","直接改配发送失败"), 8 MT8204_SEND_ERR("8204404","直接改配发送失败"),
  9 + REAPET_SUBMIT_ERR("400001","请勿重复提交,请于5秒后尝试"),
9 MT8204_BATCH_SUCCESS("200","直接改配批量申请操作成功"); 10 MT8204_BATCH_SUCCESS("200","直接改配批量申请操作成功");
10 private String code; 11 private String code;
11 private String msg; 12 private String msg;
1 package com.tianbo.analysis.dao; 1 package com.tianbo.analysis.dao;
2 2
3 import com.tianbo.analysis.model.SENDPLAN; 3 import com.tianbo.analysis.model.SENDPLAN;
  4 +import org.apache.ibatis.annotations.Param;
  5 +
  6 +import java.util.List;
4 7
5 public interface SENDPLANMapper { 8 public interface SENDPLANMapper {
6 int deleteByPrimaryKey(String carrier); 9 int deleteByPrimaryKey(String carrier);
@@ -8,4 +11,6 @@ public interface SENDPLANMapper { @@ -8,4 +11,6 @@ public interface SENDPLANMapper {
8 int insert(SENDPLAN record); 11 int insert(SENDPLAN record);
9 12
10 int insertSelective(SENDPLAN record); 13 int insertSelective(SENDPLAN record);
11 -}  
  14 +
  15 + List<SENDPLAN> list(@Param("carrier") String carrier);
  16 +}
  1 +package com.tianbo.analysis.tools;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.apache.commons.codec.digest.DigestUtils;
  5 +
  6 +import java.util.Objects;
  7 +import java.util.concurrent.ConcurrentHashMap;
  8 +import java.util.concurrent.ScheduledThreadPoolExecutor;
  9 +import java.util.concurrent.ThreadPoolExecutor;
  10 +import java.util.concurrent.TimeUnit;
  11 +
  12 +/**
  13 + *
  14 + * @author mrz
  15 + * 重复提交判定锁
  16 + */
  17 +@Slf4j
  18 +public final class ResubmitLock {
  19 + private static final ConcurrentHashMap<String, Object> LOCK_CACHE = new ConcurrentHashMap<>(200);
  20 + private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(5, new ThreadPoolExecutor.DiscardPolicy());
  21 +// private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
  22 + // 最大缓存 100 个
  23 + // .maximumSize(1000)
  24 + // 设置写缓存后 5 秒钟过期
  25 + // .expireAfterWrite(5, TimeUnit.SECONDS)
  26 + // .build();
  27 +
  28 +
  29 + private ResubmitLock() {
  30 + }
  31 +
  32 + /**
  33 + * 静态内部类 单例模式
  34 + *
  35 + * @return
  36 + */
  37 + private static class SingletonInstance {
  38 + private static final ResubmitLock INSTANCE = new ResubmitLock();
  39 + }
  40 +
  41 + public static ResubmitLock getInstance() {
  42 + return SingletonInstance.INSTANCE;
  43 + }
  44 +
  45 +
  46 + public static String handleKey(String param) {
  47 + return DigestUtils.md5Hex(param == null ? "" : param);
  48 + }
  49 +
  50 + /**
  51 + * 加锁 putIfAbsent 是原子操作保证线程安全
  52 + *
  53 + * @param key 对应的key
  54 + * @param value
  55 + * @return
  56 + */
  57 + public boolean lock(final String key, Object value) {
  58 + return Objects.isNull(LOCK_CACHE.putIfAbsent(key, value));
  59 + }
  60 +
  61 + /**
  62 + * 延时释放锁 用以控制短时间内的重复提交
  63 + *
  64 + * @param lock 是否需要解锁
  65 + * @param key 对应的key
  66 + * @param delaySeconds 延时时间
  67 + */
  68 + public void unLock(final boolean lock, final String key, final int delaySeconds) {
  69 + if (lock) {
  70 + EXECUTOR.schedule(() -> {
  71 + LOCK_CACHE.remove(key);
  72 + }, delaySeconds, TimeUnit.SECONDS);
  73 + }
  74 + }
  75 +}
@@ -446,7 +446,8 @@ GROUP BY @@ -446,7 +446,8 @@ GROUP BY
446 createdate, 446 createdate,
447 status AS status, 447 status AS status,
448 receiptinformation, 448 receiptinformation,
449 - 'MT1201' AS messagetype 449 + 'MT1201' AS messagetype,
  450 + PRODUCTNAME
450 FROM 451 FROM
451 originmanifestmaster UNION ALL 452 originmanifestmaster UNION ALL
452 SELECT 453 SELECT
@@ -462,7 +463,8 @@ GROUP BY @@ -462,7 +463,8 @@ GROUP BY
462 createdate, 463 createdate,
463 status, 464 status,
464 receiptinformation, 465 receiptinformation,
465 - talltype AS messagetype 466 + talltype AS messagetype,
  467 + PRODUCTNAME
466 FROM 468 FROM
467 tallymaster 469 tallymaster
468 WHERE 470 WHERE
@@ -494,7 +496,8 @@ GROUP BY @@ -494,7 +496,8 @@ GROUP BY
494 createdate, 496 createdate,
495 status, 497 status,
496 receiption as receiptinformation, 498 receiption as receiptinformation,
497 - 'MT1201-1' as messagetype 499 + 'MT1201-1' as messagetype,
  500 + PRODUCTNAME
498 from originmanifestsecondary 501 from originmanifestsecondary
499 where originmanifestmasterautoid= #{autoid} 502 where originmanifestmasterautoid= #{autoid}
500 UNION ALL 503 UNION ALL
@@ -506,7 +509,8 @@ GROUP BY @@ -506,7 +509,8 @@ GROUP BY
506 createdate, 509 createdate,
507 status, 510 status,
508 receiptinformation, 511 receiptinformation,
509 - 'MT5201-1' as messagetype 512 + 'MT5201-1' as messagetype,
  513 + PRODUCTNAME
510 from tallysecondary 514 from tallysecondary
511 where tallymasterid= #{autoid} 515 where tallymasterid= #{autoid}
512 </select> 516 </select>
@@ -590,7 +590,8 @@ select * from( @@ -590,7 +590,8 @@ select * from(
590 status AS status, 590 status AS status,
591 concat( receiptinformation, arrived_ahead ) AS receiptinformation, 591 concat( receiptinformation, arrived_ahead ) AS receiptinformation,
592 'MT2201' AS messagetype, 592 'MT2201' AS messagetype,
593 - customsstatus 593 + customsstatus,
  594 + PRODUCTNAME
594 FROM 595 FROM
595 preparemaster 596 preparemaster
596 <include refid="TreeCondition" /> 597 <include refid="TreeCondition" />
@@ -609,7 +610,8 @@ select * from( @@ -609,7 +610,8 @@ select * from(
609 status, 610 status,
610 receiptinformation, 611 receiptinformation,
611 'MT3201' AS messagetype, 612 'MT3201' AS messagetype,
612 - arrived_ahead AS customsstatus 613 + arrived_ahead AS customsstatus,
  614 + PRODUCTNAME
613 FROM 615 FROM
614 arrivedmaster 616 arrivedmaster
615 <include refid="TreeCondition" /> 617 <include refid="TreeCondition" />
@@ -628,7 +630,8 @@ select * from( @@ -628,7 +630,8 @@ select * from(
628 status, 630 status,
629 receiption AS receiptinformation, 631 receiption AS receiptinformation,
630 'MT4201' AS messagetype, 632 'MT4201' AS messagetype,
631 - '' AS customsstatus 633 + '' AS customsstatus,
  634 + PRODUCTNAME
632 FROM 635 FROM
633 departuresloading 636 departuresloading
634 <where> 637 <where>
@@ -660,7 +663,8 @@ select * from( @@ -660,7 +663,8 @@ select * from(
660 status, 663 status,
661 receiptinformation, 664 receiptinformation,
662 talltype AS messagetype, 665 talltype AS messagetype,
663 - '' AS customsstatus 666 + '' AS customsstatus,
  667 + PRODUCTNAME
664 FROM 668 FROM
665 tallymaster 669 tallymaster
666 <where> 670 <where>
@@ -694,7 +698,8 @@ select * from( @@ -694,7 +698,8 @@ select * from(
694 status, 698 status,
695 receiptinformation, 699 receiptinformation,
696 'MT2201-1' as messagetype, 700 'MT2201-1' as messagetype,
697 - customsstatus 701 + customsstatus,
  702 + PRODUCTNAME
698 from preparesecondary 703 from preparesecondary
699 where PREPAREMASTERID= #{autoid} 704 where PREPAREMASTERID= #{autoid}
700 705
@@ -709,7 +714,8 @@ select * from( @@ -709,7 +714,8 @@ select * from(
709 status, 714 status,
710 receiption as receiptinformation, 715 receiption as receiptinformation,
711 'MT3201-1' as messagetype, 716 'MT3201-1' as messagetype,
712 - '' as customsstatus 717 + '' as customsstatus,
  718 + PRODUCTNAME
713 from arrivedsecondary 719 from arrivedsecondary
714 where ARRIVEDMASTERID= #{autoid} 720 where ARRIVEDMASTERID= #{autoid}
715 UNION ALL 721 UNION ALL
@@ -723,7 +729,8 @@ select * from( @@ -723,7 +729,8 @@ select * from(
723 status, 729 status,
724 receiptinformation, 730 receiptinformation,
725 'MT5202-1' as messagetype, 731 'MT5202-1' as messagetype,
726 - '' as customsstatus 732 + '' as customsstatus,
  733 + PRODUCTNAME
727 from tallysecondary 734 from tallysecondary
728 where tallymasterID= #{autoid} 735 where tallymasterID= #{autoid}
729 </select> 736 </select>
@@ -25,4 +25,11 @@ @@ -25,4 +25,11 @@
25 </if> 25 </if>
26 </trim> 26 </trim>
27 </insert> 27 </insert>
28 -</mapper>  
  28 +
  29 + <select id="list" parameterType="java.lang.String" resultMap="BaseResultMap">
  30 + select CARRIER from CGONMS.SENDPLAN
  31 + <if test="carrier != null and carrier != ''" >
  32 + where CARRIER = #{carrier,jdbcType=VARCHAR}
  33 + </if>
  34 + </select>
  35 +</mapper>