3

I need help to implement hits per day based on the user selected package. This is what I made so far but it's not working properly:

Entity:

@Entity
@Table(name = "users")
public class UsersModel implements Serializable {

    @Column(name = "plan")
    private String plan;

    @Column(name = "plan_last_activity")
    @Convert(converter = LocalDateTimeConverter.class)
    private LocalDateTime planLastActivity;
}

Code:

public boolean calculateClickTimes() {

    String userName = SecurityUtils.getSubject().getPrincipal().toString();
    QueryDashboardHelper queryHelper = new QueryDashboardHelper();
    UsersModel user = queryHelper.getUserByUserName(userName);

    String plan = user.getPlan(); // silver/gold/platinum/diamond
    int todayHits = user.getTradesPerDay();
    LocalDateTime lastHit = user.getPlanLastActivity();

    LocalDateTime now = LocalDateTime.now();
    LocalDateTime tenSecondsLater = now.plusDays(1);

    long diff = ChronoUnit.DAYS.between(lastHit, tenSecondsLater);

    switch(plan) {

      case "diamond":

        if(diff >= 1 && todayHits >= 20) {
            todayHits = 0;
            return true;
        }       
        break;

      case "platinum":

        if(diff >= 1 && todayHits >= 15) {
            todayHits = 0;
            return true;
        }       
        break;  

      case "gold":

        if(diff >= 1 && todayHits >= 10) {
            todayHits = 0;
            return true;
        }             
        break;  

      case "silver":

        if(diff >= 1 && todayHits >= 5) {
            // User has clicked 5 times today
            todayHits = 0;
            return true;
        }
        break;

      default:

    }

    return false;
}

The general idea is that users should be limited to perform hits in web page based on the selected package (silver/gold/platinum/diamond) and based on the data from the database plan, tradesPerDay and planLastActivity the allowed hits should be limited for the current day. Can you give me some advice how to implement this code properly, please?

Andronicus
  • 25,419
  • 17
  • 47
  • 88
Peter Penzov
  • 1,126
  • 134
  • 430
  • 808
  • The first and obvious mistake in the code above that the condition for a _silver_ plan is also true for all other plans, i.e `todayHits >= 5` is true for a `todayHits >= 10`, etc. So you should change cases by falling down values: case "diamond" -> case "platinum" etc. – Yuriy Tsarkov Jan 19 '20 at 14:27
  • 1
    You might want to revisit [this](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/if.html). – Kayaman Jan 19 '20 at 14:36
  • I updated the code. Can you give me some more advice how to implement this please? – Peter Penzov Jan 19 '20 at 19:43

2 Answers2

2

it's a suggestion mechanism skeleton that can give you idea about using Spring aspect to handle this kind of scenarios

Create an annotation e.g TrackHit:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TrackHit{
    
}

and create an aspect

@Aspect
@Component
public class HitTrackerAspect {
    private final HitTrackerService hitTrackerService;

    public HitTrackerAspect(HitTrackerService hitTrackerService) {
        this.hitTrackerService = hitTrackerService;
    }
    
    // annotation package also should provided if present e.g @annotation(x.y.TrackHit)
    @Around(value = "@annotation(TrackHit)")
    public Object TrackUserHit(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        TrackHit trackHit = method.getAnnotation(TrackHit.class);
        if(trackHit !=null){
            String userName = SecurityUtils.getSubject().getPrincipal().toString();
            if (userName!=null) {
                // hitTrack service is used to check user plan , logic implemented inside isUserAllowed method 
                // of HitTrackerService 
                boolean allowed = hitTrackerService.isUserAllowed(userName)
                if(allowed){
                    return proceedingJoinPoint.proceed();
                }
            }
        }
    }
}

in services that need to be invoked by user add TrackHit annotation on it , aspect will check if allowed will proceed

e.g calling latest price limited by aspect login and should checked before invoked

@Service
public class PriceInquiryService {
    
    @TrackHit
    public long getLatestPrice(Long itemCode){
        // latest price logic here 
    }
}
Saeed Alizadeh
  • 1,417
  • 13
  • 23
  • Thanks. But what algorithm can you propose to limit the hits per day? – Peter Penzov Jan 22 '20 at 08:10
  • using a hit history table, user => hit timestamp maping, each time i will insert a new record in it inside sample aspect method i provided above each time api called by user. when i want to check i will call a aggregate query of count that is limited by user id and time between start of today time and end time of today mean today 00:00 to today 23:59 then check my plan according to number of hit count of a user during latest 24 hours – Saeed Alizadeh Jan 22 '20 at 08:23
1

You can do it on the database level:

create table users (
    id bigint primary key ,
    plan character varying,
    trades_today int
);

insert into users (id, plan, trades_today)
values (1, 'diamond', 15),
       (2, 'silver', 5),
       (3, 'gold', 0);

with packages(package_type, max_hits) as (
    values ('diamond', 20),
           ('platinum', 15),
           ('gold', 10),
           ('silver', 5)
)

select u.id, trades_today < p.max_hits
from users u
inner join packages p on u.plan = p.package_type;

This results in:

+--+--------+
|id|?column?|
+--+--------+
|1 |true    |
|2 |false   |
|3 |true    |
+--+--------+

Every time a hit is done, the counter is increased. The counter is reset every midnight

Andronicus
  • 25,419
  • 17
  • 47
  • 88