Implementing Branch Performance Aggregation and Therapist Performance View Features
Aggregate Real Monthly and Annual Branch Performance
Implementation approach: Sum the performance of all therapists assigned to the branch to get total branch performance.
No frontend changes required.
Backend Implementation
// Accumulate therapist performance points to the branch's monthly and annual total performance
// Query the branch associated with the assigned therapist
String branchId = therapistService.getBranchByTherapistId(regularAppointment.getAssignedTherapistId());
// Update cumulative performance in branch monthly and annual statistics tables
int monthlyUpdateResult = monthlyBranchStatsService.updateMonthlyPerformance(branchId, Integer.parseInt(projectPoints));
int annualUpdateResult = annualBranchStatsService.updateAnnualPerformance(branchId, Integer.parseInt(projectPoints));
System.out.println("Annual update result: " + annualUpdateResult);
System.out.println("Monthly update result: " + monthlyUpdateResult);
System.out.println("Target branch ID: " + branchId);
// Repeat logic for member appointment records
// Accumulate therapist performance points to the branch's monthly and annual total performance
// Query the branch associated with the assigned therapist
String branchId = therapistService.getBranchByTherapistId(memberAppointment.getAssignedTherapistId());
System.out.println("Target branch ID: " + branchId);
// Update cumulative performance in branch monthly and annual statistics tables
int monthlyUpdateResult = monthlyBranchStatsService.updateMonthlyPerformance(branchId, Integer.parseInt(projectPoints));
int annualUpdateResult = annualBranchStatsService.updateAnnualPerformance(branchId, Integer.parseInt(projectPoints));
System.out.println("Annual update result: " + annualUpdateResult);
System.out.println("Monthly update result: " + monthlyUpdateResult);
Update Monthly and Annual Therapy Project Booking & Revenue Statistics
Implementation approach: After an admin approves an appointment, increment the project's booking count by 1, and add the project price to the cumulative sales revenue, for both monthly and annual statistics.
No frontend changes required.
Backend Implementation
// Update monthly and annual booking count and revenue for the reserved therapy project
// First check if a statistical record already exists for the project
int monthlyExistingCount = projectMonthlyStatsService.checkProjectExists(regularAppointment.getProjectName());
int annualExistingCount = projectAnnualStatsService.checkProjectExists(regularAppointment.getProjectName());
System.out.println("Annual existing record count: " + annualExistingCount);
System.out.println("Monthly existing record count: " + monthlyExistingCount);
// Update existing record, insert new if not exists
if (monthlyExistingCount != 0) {
// Update monthly booking count and total revenue
projectMonthlyStatsService.incrementMonthlyStats(regularAppointment.getProjectName(), projectPrice);
} else {
TherapyProjectMonthlyStat monthlyStat = new TherapyProjectMonthlyStat();
monthlyStat.setId(System.currentTimeMillis() + (long) (Math.random() * 1000));
monthlyStat.setCreateTime(new DateTime());
monthlyStat.setStatDate(new DateTime());
monthlyStat.setProjectName(regularAppointment.getProjectName());
monthlyStat.setProjectCover(projectImageUrl);
monthlyStat.setMonthlyBookingCount(1);
monthlyStat.setMonthlyRevenue(Integer.parseInt(projectPoints));
projectMonthlyStatsService.insert(monthlyStat);
}
// Process annual statistics
if (annualExistingCount != 0) {
// Update annual booking count and total revenue
projectAnnualStatsService.incrementAnnualStats(regularAppointment.getProjectName(), projectPrice);
} else {
TherapyProjectAnnualStat annualStat = new TherapyProjectAnnualStat();
annualStat.setId(System.currentTimeMillis() + (long) (Math.random() * 1000));
annualStat.setCreateTime(new DateTime());
annualStat.setStatDate(new DateTime());
annualStat.setProjectName(regularAppointment.getProjectName());
annualStat.setProjectCover(projectImageUrl);
annualStat.setAnnualBookingCount(1);
annualStat.setAnnualRevenue(Integer.parseInt(projectPoints));
projectAnnualStatsService.insert(annualStat);
}
// Get project cover image for member appointment records
String projectImageUrl = therapyProjectService.getProjectCover(memberAppointment.getProjectName());
System.out.println("Member annual existing count: " + annualExistingCount);
System.out.println("Member monthly existing count: " + monthlyExistingCount);
if (monthlyExistingCount != 0) {
projectMonthlyStatsService.incrementMonthlyStats(memberAppointment.getProjectName(), projectPrice);
} else {
TherapyProjectMonthlyStat monthlyStat = new TherapyProjectMonthlyStat();
monthlyStat.setId(System.currentTimeMillis() + (long) (Math.random() * 1000));
monthlyStat.setCreateTime(new DateTime());
monthlyStat.setStatDate(new DateTime());
monthlyStat.setProjectName(memberAppointment.getProjectName());
monthlyStat.setProjectCover(projectImageUrl);
monthlyStat.setMonthlyBookingCount(1);
monthlyStat.setMonthlyRevenue(Integer.parseInt(projectPoints));
projectMonthlyStatsService.insert(monthlyStat);
}
// Process annual statistics for member appointments
if (annualExistingCount != 0) {
projectAnnualStatsService.incrementAnnualStats(memberAppointment.getProjectName(), projectPrice);
} else {
TherapyProjectAnnualStat annualStat = new TherapyProjectAnnualStat();
annualStat.setId(System.currentTimeMillis() + (long) (Math.random() * 1000));
annualStat.setCreateTime(new DateTime());
annualStat.setStatDate(new DateTime());
annualStat.setProjectName(memberAppointment.getProjectName());
annualStat.setProjectCover(projectImageUrl);
annualStat.setAnnualBookingCount(1);
annualStat.setAnnualRevenue(Integer.parseInt(projectPoints));
projectAnnualStatsService.insert(annualStat);
}
Update Multiple Columns in Single SQL Udpate Statement
UPDATE target_table
SET column_one = target_value_1,
column_two = target_value_2
WHERE match_condition;
Therapist Personal Performance View
Implementation approach: Create a new Vue page by copying an existing ECharts template. Backend queries all matching appointments (both regular user and member) assigned to the current logged in therapist, frontend aggregates and displays data grouped by project name, following the implementation pattern of existing appointment pages.
Get Current Logged In Username in Vue
Implementation approach: Retrieve the logged in account name from local storage, pass it as a parameter to the backend API.
Frontend Code
// Fetch therapist info by logged in account
getTherapistInfo(accountName) {
this.$http({
url: `therapist/name/${accountName}`,
method: 'get'
}).then(res => {
console.log(res.data.data);
this.therapistFullName = res.data.data;
console.log("Therapist name query succeeded");
})
}
Backend Code
/**
* Query therapist full name by therapist account
*/
@GetMapping("/name/{accountName}")
public R getTherapistName(@PathVariable("accountName") String account) {
String fullName = therapistService.queryTherapistNameByAccount(account);
return R.ok().put("data", fullName);
}
MyBatis Mapper:
<select id="queryTherapistNameByAccount" resultType="java.lang.String">
SELECT therapist_full_name
FROM therapist
WHERE therapist_work_number = #{account};
</select>
Therapist Management & Performance Page for Branch Admin
Implementation approach: Query the branch associated with the logged in branch admin account, pass the branch identifier as a parameter to fetch performance data for all therapists under that branch, then render the performance chart.
Frontend Code
console.log("Logged in branch admin account: ");
console.log(this.$storage.get('adminName'));
this.renderMonthlyChart();
let currentAccount = this.$storage.get('adminName');
if (!this.branchName) {
this.$http({
url: `branch/info/${currentAccount}`,
method: 'get'
}).then(res => {
console.log(res.data.data);
this.branchName = res.data.data;
this.fetchTherapistPerformanceData();
this.renderAnnualChart();
}).catch(err => {
console.log(err);
console.log("Failed to fetch branch information");
})
} else {
this.fetchTherapistPerformanceData();
this.renderAnnualChart();
}
function fetchTherapistPerformanceData() {
this.$http({
url: `therapist/annual/stats/branch/${this.branchName}`,
method: 'get'
}).then(res => {
console.log(res.data);
let therapistStats = res.data.therapistAnnualStats;
let performanceData = [];
for (let i = 0; i < therapistStats.length; i++) {
let item = { name: '', value: 0 };
item.name = therapistStats[i].therapistName;
item.value = therapistStats[i].annualPerformance;
performanceData.push(item);
}
// Sort descending by performance value
performanceData.sort((a, b) => b.value - a.value);
this.performanceData = performanceData;
this.renderMonthlyChart();
}).catch(err => {
console.log(err);
console.log("Failed to fetch therapist performance data");
})
}
Backend Code
/**
* Get annual performance data for all therapists under a branch for pie chart rendering
*/
@GetMapping("/branch/performance/{branchName}")
public Map<String, List<TherapistAnnualStatEntity>> getBranchTherapistPerformance(@PathVariable("branchName") String branchName) {
HashMap<String, List<TherapistAnnualStatEntity>> resultMap = new HashMap<>();
// Get all therapist names assigned to the target branch
List<String> therapistList = therapistService.getTherapistNamesByBranch(branchName);
for (int i = 0; i < therapistList.size(); i++) {
System.out.println("Therapist " + i + ": " + therapistList.get(i));
}
// Query matching performance records from the annual statistics table
resultMap.put("therapistAnnualStats", therapistAnnualStatsService.queryTherapistPerformanceByNames(therapistList));
return resultMap;
}