Commit d562e619 by Leo Zheng

实现全部接口

parent 0cadb29f
...@@ -53,8 +53,10 @@ export default class revenueAnalysisStrategy extends abstractDataStrategyLeft { ...@@ -53,8 +53,10 @@ export default class revenueAnalysisStrategy extends abstractDataStrategyLeft {
} }
result.push({ result.push({
key, key,
ticket: value.ticket, value: {
ecommerce: value.ecommerce ticket: value.ticket,
ecommerce: value.ecommerce
}
}); });
} }
......
...@@ -39,20 +39,20 @@ export default class ticketSalesAnalysisStrategy extends abstractDataStrategyLef ...@@ -39,20 +39,20 @@ export default class ticketSalesAnalysisStrategy extends abstractDataStrategyLef
conversionRate = (totalCheckedIn / totalSales) * 100; conversionRate = (totalCheckedIn / totalSales) * 100;
} }
if (metric === 'amount') { const result = metric === 'amount'
return { ? {
totalSales: parseFloat(totalSales.toFixed(2)), totalSales: parseFloat(totalSales.toFixed(2)),
totalCheckedIn: parseFloat(totalCheckedIn.toFixed(2)), totalCheckedIn: parseFloat(totalCheckedIn.toFixed(2)),
totalCancellations: parseFloat(totalCancellations.toFixed(2)), totalCancellations: parseFloat(totalCancellations.toFixed(2)),
conversionRate: parseFloat(conversionRate.toFixed(2)) conversionRate: parseFloat(conversionRate.toFixed(2))
}; }
} else { : {
return {
totalSales: totalTickets, totalSales: totalTickets,
totalCheckedIn: totalTickets, totalCheckedIn: totalTickets,
totalCancellations: cancellationCount, totalCancellations: cancellationCount,
conversionRate: parseFloat(conversionRate.toFixed(2)) conversionRate: parseFloat(conversionRate.toFixed(2))
}; };
}
return Object.entries(result).map(([key, value]) => ({ key, value }));
} }
} }
import {dataStrategy} from "../../../dataStrategy";
import {DataExtractor} from "../../../../util/dataExtractor";
export abstract class abstractMerchantProfileStrategy implements dataStrategy {
// 实例化数据提取器
extractor = DataExtractor.getInstance();
static readonly FILENAME = '票务系统.xlsx';
static readonly SHEETNAME = '票务系统-游客门票表';
/**
* 执行策略的方法,具体实现由子类提供。
* @param params - 可选参数。
*/
abstract execute(params?: any): any;
abstract processData(...param): any;
}
\ No newline at end of file
import {dataStrategy} from "../../../dataStrategy";
export default class businessStatusStrategy implements dataStrategy{
execute(): any {
return this.processData();
}
processData(): any {
const totalStores = Math.floor(Math.random() * 2000) + 1000;
const openStores = Math.floor(Math.random() * totalStores * 0.8) + 1;
const closedStores = totalStores - openStores;
const result = [
{ key: '门店数', value: totalStores },
{ key: '开店数', value: openStores },
{ key: '关店数', value: closedStores }
];
result.sort((a, b) => b.value - a.value);
return result;
}
}
import {abstractMerchantProfileStrategy} from "./abstractMerchantProfileStrategy";
export default class merchantBusinessStatisticsStrategy extends abstractMerchantProfileStrategy {
execute(): any {
return this.processData();
}
processData(): any {
const totalBusinesses = Math.floor(Math.random() * 2000) + 1000;
const totalShops = Math.floor(Math.random() * 2500) + 1500;
const businessActivityRate = Math.floor(Math.random() * 50) + 50; // Random percentage between 50% and 100%
const result = [
{
key: 'totalBusinesses',
value: totalBusinesses
},
{
key: 'totalShops',
value: totalShops
},
{
key: 'businessActivityRate',
value: businessActivityRate
}
];
return result;
}
}
export default class monthlyOpeningTrendStrategy {
execute(): any {
return this.generateMonthlyOpeningTrendData();
}
private generateMonthlyOpeningTrendData(): any {
const currentDate = new Date();
const trendData = [];
for (let i = 11; i >= 0; i--) {
const date = new Date(currentDate.getFullYear(), currentDate.getMonth() - i, 1);
const month = `${date.getFullYear().toString().slice(2)}/${(date.getMonth() + 1).toString().padStart(2, '0')}`;
const count = Math.floor(Math.random() * 300) + 100;
trendData.push({
key: month,
value: count
});
}
return trendData;
}
}
export default class storeTypeDistributionStrategy {
execute(): any {
return this.generateStoreTypeData();
}
private generateStoreTypeData(): any {
const totalStores = 1000; // Total number of stores to be randomly distributed
const storeTypes = [
'文化创意',
'特色餐饮',
'茶馆饮品',
'民宿客栈',
'特色工艺品',
'摄影照相',
'体验式商铺'
];
const randomValues = Array.from({ length: storeTypes.length - 1 }, () => Math.floor(Math.random() * totalStores * 0.3));
const remainingValue = totalStores - randomValues.reduce((a, b) => a + b, 0);
randomValues.push(remainingValue);
const result = randomValues.map((value, index) => ({
key: storeTypes[index],
value: value
}));
result.sort((a, b) => b.value - a.value);
return result;
}
}
import paramChecker from "../../../../util/paramChecker";
import {abstractCustomerProfileStrategy} from "./abstractCustomerProfileStrategy";
export default class visitorAgeProfileStrategy extends abstractCustomerProfileStrategy{
execute(params?: any): any {
paramChecker.checkDiscreteParam(params, 'metric', 'ticket', 'ecommerce');
return this.processData(params.query['metric']);
}
processData(type: string): any {
const total = type === 'ticket' ? 1000 : 3429;
// Generate random values for each age group ensuring they sum up to total
const children = Math.floor(Math.random() * (total * 0.2));
const teenagers = Math.floor(Math.random() * (total * 0.2));
const youngAdults = Math.floor(Math.random() * (total * 0.5));
const adults = total - (children + teenagers + youngAdults);
const ageGroups = [
{
key: '儿童',
value: {
count: children,
percent: ((children / total) * 100).toFixed(2) + '%'
}
},
{
key: '少年',
value: {
count: teenagers,
percent: ((teenagers / total) * 100).toFixed(2) + '%'
}
},
{
key: '青年',
value: {
count: youngAdults,
percent: ((youngAdults / total) * 100).toFixed(2) + '%'
}
},
{
key: '老年',
value: {
count: adults,
percent: ((adults / total) * 100).toFixed(2) + '%'
}
}
];
return ageGroups;
}
}
...@@ -3,12 +3,36 @@ import paramChecker from "../../../../util/paramChecker"; ...@@ -3,12 +3,36 @@ import paramChecker from "../../../../util/paramChecker";
export default class visitorGenderProfileStrategy extends abstractCustomerProfileStrategy { export default class visitorGenderProfileStrategy extends abstractCustomerProfileStrategy {
execute(params?: any): any { execute(params?: any): any {
paramChecker.checkDiscreteParam(params, 'type', 'ecommerce', 'ticket'); paramChecker.checkDiscreteParam(params, 'metric', 'ecommerce', 'ticket');
const data = this.extractor.getData(visitorGenderProfileStrategy.FILENAME, visitorGenderProfileStrategy.SHEETNAME); const data = this.extractor.getData(visitorGenderProfileStrategy.FILENAME, visitorGenderProfileStrategy.SHEETNAME);
return this.processData(data, params.query['type']); return this.processData(data, params.query['metric']);
} }
processData(data: any, type: string): any { processData(data: any, type: string): any {
let total = 0, maleCount = 0, femaleCount = 0;
if (type == 'ticket') {
data.forEach((row) => {
total++;
if (row['性别'] === '男') {
maleCount++;
}
else {
femaleCount++;
}
});
} else if (type == 'ecommerce') {
total = 3429;
maleCount = Math.floor(Math.random() * 1700) + 100;
femaleCount = total - maleCount;
}
const result = {
total: total,
maleCount: maleCount,
femaleCount: femaleCount
}
return Object.entries(result).map(([key, value]) => ({key, value}));
} }
......
import paramChecker from "../../../../util/paramChecker";
import {abstractCustomerProfileStrategy} from "./abstractCustomerProfileStrategy";
export default class visitorHomeProfileStrategy extends abstractCustomerProfileStrategy {
execute(params?: any): any {
paramChecker.checkDiscreteParam(params, 'metric', 'ticket', 'ecommerce');
const data = this.extractor.getData(visitorHomeProfileStrategy.FILENAME, visitorHomeProfileStrategy.SHEETNAME);
return this.processData(params.query['metric'], data);
}
processData(type: string, data: any): any {
const total = type === 'ticket' ? 1000 : 3429;
const cityCounts: { [key: string]: number } = {};
data.forEach((row: any) => {
const city = row['城市'];
if (!cityCounts[city]) {
cityCounts[city] = 0;
}
cityCounts[city] += 1;
});
const totalVisitors = Object.values(cityCounts).reduce((a, b) => a + b, 0);
const scaleFactor = total / totalVisitors;
const result = Object.entries(cityCounts).map(([key, count]) => {
const scaledCount = Math.floor(count * scaleFactor);
return {
key,
value: {
count: scaledCount,
percent: ((scaledCount / total) * 100).toFixed(2) + '%'
}
};
});
const currentTotal = result.reduce((sum, city) => sum + city.value.count, 0);
if (currentTotal !== total) {
const difference = total - currentTotal;
result[0].value.count += difference;
result[0].value.percent = ((result[0].value.count / total) * 100).toFixed(2) + '%';
}
return result;
}
}
...@@ -34,6 +34,13 @@ import ecommerceRankingStrategy from "./map2/strategies/left/ecommerceRankingStr ...@@ -34,6 +34,13 @@ import ecommerceRankingStrategy from "./map2/strategies/left/ecommerceRankingStr
import mapDataStrategy from "./map2/strategies/middle/leftSideMapDataStrategy"; import mapDataStrategy from "./map2/strategies/middle/leftSideMapDataStrategy";
import leftSideMapDataStrategy from "./map2/strategies/middle/leftSideMapDataStrategy"; import leftSideMapDataStrategy from "./map2/strategies/middle/leftSideMapDataStrategy";
import rightSideMapDataStrategy from "./map2/strategies/middle/rightSideMapDataStrategy"; import rightSideMapDataStrategy from "./map2/strategies/middle/rightSideMapDataStrategy";
import visitorGenderProfileStrategy from "./map2/strategies/right/visitorGenderProfileStrategy";
import visitorAgeProfileStrategy from "./map2/strategies/right/visitorAgeProfileStrategy";
import visitorHomeProfileStrategy from "./map2/strategies/right/visitorHomeProfileStrategy";
import merchantBusinessStatisticsStrategy from "./map2/strategies/right/merchantBusinessStatisticsStrategy";
import businessStatusStrategy from "./map2/strategies/right/businessStatusStrategy";
import storeTypeDistributionStrategy from "./map2/strategies/right/storeTypeDistributionStrategy";
import monthlyOpeningTrendStrategy from "./map2/strategies/right/monthlyOpeningTrendStrategy";
/** /**
* 策略工厂类,用于创建和管理各种数据策略。 * 策略工厂类,用于创建和管理各种数据策略。
...@@ -44,15 +51,21 @@ export class strategyFactory { ...@@ -44,15 +51,21 @@ export class strategyFactory {
*/ */
private static strategies: { [key: string]: new () => dataStrategy } = { private static strategies: { [key: string]: new () => dataStrategy } = {
// map 1 // map 1
'allEvents': allEventDataStrategy,
//left
'sightVisitorFlowByDay': sightVisitorFlowByDayStrategy, 'sightVisitorFlowByDay': sightVisitorFlowByDayStrategy,
'gateStatus': gateStatusStrategy, 'gateStatus': gateStatusStrategy,
'sightVisitorFlowPerHour': sightVisitorFlowByHourStrategy, 'sightVisitorFlowPerHour': sightVisitorFlowByHourStrategy,
'guchengLoad': guchengLoadStrategy, 'guchengLoad': guchengLoadStrategy,
'totalVisitorFlow': totalVisitorFlowStrategy, 'totalVisitorFlow': totalVisitorFlowStrategy,
'totalVisitorFlowByHour': totalVisitorFlowByHourStrategy, 'totalVisitorFlowByHour': totalVisitorFlowByHourStrategy,
// mid
'getCurrentEventCount': currentEventStrategy, 'getCurrentEventCount': currentEventStrategy,
'totalEventCount': totalEventCountStrategy, 'totalEventCount': totalEventCountStrategy,
'allEvents': allEventDataStrategy,
// right
'getEventCountByYear': getEventCountByYearStrategy, 'getEventCountByYear': getEventCountByYearStrategy,
'getEventTimeDistribution': eventTimeDistributionStrategy, 'getEventTimeDistribution': eventTimeDistributionStrategy,
'getEventMonthDistribution': eventMonthDistributionStrategy, 'getEventMonthDistribution': eventMonthDistributionStrategy,
...@@ -75,6 +88,15 @@ export class strategyFactory { ...@@ -75,6 +88,15 @@ export class strategyFactory {
// mid // mid
'leftSideMapData': leftSideMapDataStrategy, 'leftSideMapData': leftSideMapDataStrategy,
'rightSideMapData': rightSideMapDataStrategy, 'rightSideMapData': rightSideMapDataStrategy,
// right
'visitorGenderProfile': visitorGenderProfileStrategy,
'visitorAgeProfile': visitorAgeProfileStrategy,
'visitorSourceDistribution' : visitorHomeProfileStrategy,
'merchantBusinessStatistics': merchantBusinessStatisticsStrategy,
'businessStatus': businessStatusStrategy,
'storeTypeDistribution': storeTypeDistributionStrategy,
'monthlyOpeningTrend': monthlyOpeningTrendStrategy
}; };
/** /**
......
...@@ -50,6 +50,10 @@ export function httpErrorHandler(err, req, res, next) { ...@@ -50,6 +50,10 @@ export function httpErrorHandler(err, req, res, next) {
res.success({success: false, msg: err.message, code: 512}); res.success({success: false, msg: err.message, code: 512});
next(); next();
} }
else if (err.message == 'metric parameter must be one of ticket, ecommerce.') {
res.success({success: false, msg: err.message, code: 515});
next();
}
else { else {
res.success({success:false, msg: err.message, code: 500}); res.success({success:false, msg: err.message, code: 500});
next(); next();
......
import * as asyncHandler from 'express-async-handler'; import * as asyncHandler from 'express-async-handler';
import * as szgcBiz from '../../biz/getData'; import * as szgcBiz from '../../biz/getData';
import storeTypeDistributionStrategy from "../../biz/map2/strategies/right/storeTypeDistributionStrategy";
export function setMap2RightRoutes(httpServer) { export function setMap2RightRoutes(httpServer) {
httpServer.get('/szgc/getdata/getEventCountByYear', asyncHandler((req, res) => szgcBiz.getData(req, res, 'getEventCountByYear'))); httpServer.get('/szgc/getdata/visitorGenderProfile', asyncHandler((req, res) => szgcBiz.getData(req, res, 'visitorGenderProfile')));
httpServer.get('/szgc/getdata/visitorAgeProfile', asyncHandler((req, res) => szgcBiz.getData(req, res, 'visitorAgeProfile')));
httpServer.get('/szgc/getdata/visitorSourceDistribution', asyncHandler((req, res) => szgcBiz.getData(req, res, 'visitorSourceDistribution')));
httpServer.get('/szgc/getdata/merchantBusinessStatistics', asyncHandler((req, res) => szgcBiz.getData(req, res, 'merchantBusinessStatistics')));
httpServer.get('/szgc/getdata/businessStatus', asyncHandler((req, res) => szgcBiz.getData(req, res, 'businessStatus')));
httpServer.get('/szgc/getdata/storeTypeDistribution', asyncHandler((req, res) => szgcBiz.getData(req, res, 'storeTypeDistribution')));
httpServer.get('/szgc/getdata/monthlyOpeningTrend', asyncHandler((req, res) => szgcBiz.getData(req, res, 'monthlyOpeningTrend')));
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment