Просмотр исходного кода

feature: 添加了项目数量累计统计和月末未完成项目数记录的功能

eyes4 6 месяцев назад
Родитель
Сommit
8aa387c4f8

+ 3 - 0
base-lib/src/core-models/AcsFunction.ts

@@ -129,6 +129,9 @@ export class AcsFunction extends Model {
             (1003, 201, 'remove', '删除客户信息', 'biz.customer.remove', true, 1),
             (1004, 201, 'detail', '获取客户详情', 'biz.customer.detail', true, 0),
             (1005, 201, 'stat', '获取客户统计数据', 'biz.customer.stat', true, 0),
+            
+            (1900, 281, 'count', '项目数量统计', 'report.prj.count', true, 0),
+            
 
             (2020, 302, 'get_list', '获取项目列表', 'prj.info.get_list', true, 0),
             (2021, 302, 'add', '创建项目', 'prj.info.add', true, 1),

+ 4 - 1
base-lib/src/core-models/AcsModule.ts

@@ -93,7 +93,10 @@ export class AcsModule extends Model {
             (200, 'biz', '营销接口', null, text2ltree('200'), 'biz'),
             (201, 'customer', '客户管理', 200, text2ltree('200.201'), 'biz.customer'),
 
-
+            (280, 'report', '报表', null, text2ltree('280'), 'dashboard'),
+            (281, 'prj', '项目统计', null, text2ltree('280.281'), 'dashboard.prj'),
+            
+            
             (300, 'prj', '项目管理', null, text2ltree('300'), 'prj'),
             (301, 'template', '项目模板管理', 300, text2ltree('300.301'), 'prj.template'),
             (302, 'info', '项目信息管理', 300, text2ltree('300.302'), 'prj.info'),

+ 2 - 0
base-lib/src/core-models/AcsRoleFunction.ts

@@ -109,6 +109,8 @@ export class AcsRoleFunction extends Model {
             ('admin', 1004),
             ('admin', 1005),
             
+            ('admin', 1900),
+            
             ('admin', 2020),
             ('admin', 2021),
             ('admin', 2022),

+ 47 - 0
base-lib/src/core-models/ReportPrjState.ts

@@ -0,0 +1,47 @@
+
+import {Model} from "sequelize";
+import { Sequelize, DataTypes }  from 'sequelize';
+
+/**
+ * 项目数量统计报表(每月底统计一次)
+ */
+
+export class ReportPrjState extends Model {
+    declare ts: string;
+    declare amount: number;
+    declare undone: number;
+
+    static table_name = 'tb_report_prj_stat';
+
+    static attributes = {
+        ts: {
+            type: DataTypes.DATEONLY,
+            allowNull: false,
+            comment: '日期',
+            primaryKey: true,
+        },
+        amount: {
+            type: DataTypes.INTEGER,
+            comment: '项目数量',
+        },
+        undone: {
+            type: DataTypes.INTEGER,
+            comment: '未完成数量',
+        }
+
+    }
+
+    static indexes = [
+        {fields: ['ts']},
+    ]
+
+    static associate(sequelize: Sequelize) {
+        try {
+        } catch (e) {
+            console.log(e);
+        }
+    }
+
+    static init_sqls = [
+    ]
+}

+ 2 - 0
pmr-api-gateway/deploy.sh

@@ -5,6 +5,8 @@ webpack --mode production
 
 # 上传到服务器
 scp -r ../__dist/pmr-api-gateway/index.js root@112.124.10.239:/home/pmr/pmr-api-gateway
+scp -r ../__dist/pmr-api-gateway/proxy.json root@112.124.10.239:/home/pmr/proxy.json
+scp -r ../__dist/pmr-api-gateway/config.json root@112.124.10.239:/home/pmr/config.json
 
 # 重启 Docker
 ssh root@112.124.10.239 "docker restart pmr-api-gateway && docker logs pmr-api-gateway --tail 100 -ft"

+ 1 - 1
pmr-api-gateway/proxy.json

@@ -8,7 +8,7 @@
   },
   {
     "serviceName": "pmr-biz-manager",
-    "path": ["/api/prj.*", "/api/biz.*", "/api/cfg.*"],
+    "path": ["/api/report.*", "/api/prj.*", "/api/biz.*", "/api/cfg.*"],
     "protocol": "https",
     "host": "pmr-biz-manager",
     "port": 443

+ 1 - 1
pmr-api-gateway/proxy_dev.json

@@ -8,7 +8,7 @@
   },
   {
     "serviceName": "pmr-biz-manager",
-    "path": ["/api/prj.*", "/api/biz.*", "/api/cfg.*"],
+    "path": ["/api/report.*","/api/prj.*", "/api/biz.*", "/api/cfg.*"],
     "protocol": "https",
     "host": "localhost",
     "port": 3443

+ 5 - 26
pmr-biz-manager/src/app.ts

@@ -11,14 +11,11 @@ import {Logger} from "@util/Logger";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 import {BizContractInfo} from "@core-models/BizContractInfo";
 import {PrjFile} from "@core-models/PrjFile";
-import {BpmnModel} from "@core-models/BpmnModel";
 import {FlowEngine} from "@src/bpmn/flow_engine";
-import {UploadedFiles} from "@core-models/UploadedFiles";
 import {BpmnCase} from "@core-models/BpmnCase";
 import {bpmn_flow_on_end} from "@src/utils/bpmn_work_helper";
 import {OutcomeStatus} from "@src/utils/define";
-import {Op} from "sequelize";
-import * as os from "os";
+import {prj_stat_monthly} from "@src/utils/prj_stat";
 const { report } = require('node:process');
 
 const schedule = require('node-schedule');
@@ -52,32 +49,11 @@ new ServiceApp().start(new MyRouter('@src/routes', guards), Models).then(async (
                                             returning: ['id', 'task_id'],
                                             transaction: t
                                         });
-                                        // if (result && result[0]) {
-                                        //     let outcomes = await PrjTaskOutcome.findAll({
-                                        //         where: {
-                                        //             task_id: result[0].task_id,
-                                        //             status: OutcomeStatus.New,
-                                        //         },
-                                        //     });
-                                        //     if (outcomes.length === 0) { // 所有交付物都上传完成,启动任务审核流程
-                                        //
-                                        //     }
-                                        // }
                                     }
                                     break;
                                 case 'week_report':
                                     break;
                             }
-                            // if (path[2] === 'outcome') {
-                            //     let outcome_id = path[3];
-                            //     if (outcome_id) {
-                            //         let [count, result] = await PrjTaskOutcome.update({status: OutcomeStatus.Uploaded, uploaded: true}, {
-                            //             where: {id: outcome_id},
-                            //             returning: [],
-                            //             transaction: t
-                            //         });
-                            //     }
-                            // }
                             await PrjFile.update({
                                 uploaded_at: dayjs(),
                                 uploaded: true,
@@ -146,7 +122,10 @@ new ServiceApp().start(new MyRouter('@src/routes', guards), Models).then(async (
         }, bpmn_flow_on_end).run(flow.state);
     }
 
-
+    // 项目数统计
+    schedule.scheduleJob('50 23 * * *', async function () {
+        await prj_stat_monthly();
+    });
 
     // let bpmn = await BpmnModel.findOne({where: {id: 'task'}, raw: true});
     // if (bpmn) {

+ 3 - 1
pmr-biz-manager/src/models/Models.ts

@@ -25,6 +25,7 @@ import {BpmnForm} from "@core-models/BpmnForm";
 import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {PrjTaskOutcomeDraft} from "@core-models/PrjTaskOutcomeDraft";
 import {PrjLogs} from "@core-models/PrjLogs";
+import {ReportPrjState} from "@core-models/ReportPrjState";
 
 export const Models: Array<IDataModelInfo> = [
 
@@ -51,7 +52,8 @@ export const Models: Array<IDataModelInfo> = [
     {tag: 'pmr', model: BpmnCase, timestamps: false},
     {tag: 'pmr', model: BpmnWork, timestamps: false},
     {tag: 'pmr', model: BpmnForm, timestamps: false},
-    {tag: 'pmr', model: PrjLogs, timestamps: false}
+    {tag: 'pmr', model: PrjLogs, timestamps: false},
+    {tag: 'pmr', model: ReportPrjState, timestamps: false}
     // {tag: 'pmr', model: UploadedFiles, timestamps: false}
 
 ]

+ 1 - 7
pmr-biz-manager/src/routes/api/cfg/doc_type/get_list.ts

@@ -1,11 +1,5 @@
-import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
-import {Resp} from "@util/Resp";
-import {Op, WhereOptions} from "sequelize";
+import {IApiProcessor} from "@core/Defined";
 import DataCURD from "@core/DataCURD";
-import {AcsApplication} from "@core-models/AcsApplication";
-import {BizCustomerIndustry} from "@core-models/BizCustomerIndustry";
-import {AcsRole} from "@core-models/AcsRole";
-import {PrjFile} from "@core-models/PrjFile";
 import {PrjFileCategory} from "@core-models/PrjFileCategory";
 
 const v1_0: IApiProcessor = {

+ 127 - 0
pmr-biz-manager/src/routes/api/report/prj/count.ts

@@ -0,0 +1,127 @@
+import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
+import {Resp} from "@util/Resp";
+import {QueryTypes} from "sequelize";
+import DataCURD, {ISQLReplacements} from "@core/DataCURD";
+import {PrjMembers} from "@core-models/PrjMembers";
+import {ReportPrjState} from "@core-models/ReportPrjState";
+import dayjs from "dayjs";
+
+interface IData {
+    /**
+     * 起始月份,YYYY-MM格式
+     */
+    begin_at: string;
+    /**
+     * 结束月份,YYYY-MM格式
+     */
+    end_at: string;
+}
+
+function get_list(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<any> {
+    return new Promise<any>(async (resolve, reject) => {
+        try {
+            let data = <IData>json.data;
+            let sql = `
+                select 
+                    TO_CHAR(ts, 'yyyy-MM') as ts,
+                    amount, undone
+                from ${ReportPrjState.table_name} 
+                where ts >= :begin_at and ts <= :end_at
+                order by ts asc
+            `
+            let replacements: ISQLReplacements = {
+                begin_at: dayjs(data.begin_at).startOf('month').startOf('day').format('YYYY-MM-DD'),
+                end_at: dayjs(data.end_at).endOf('month').endOf('day').format('YYYY-MM-DD'),
+            };
+            let result = await PrjMembers.sequelize!.query(sql, {type: QueryTypes.SELECT, replacements: replacements, raw: true});
+            let list = DataCURD.filter_null(result);
+            resolve({list: list});
+        } catch (e) {
+            reject(Resp.throw_err(e));
+        }
+    })
+}
+
+const v1_0: IApiProcessor = {
+    schema: {
+        "type": "object",
+        "properties": {
+            "data": {
+                "type": "object",
+                "properties": {
+                    "begin_at": {
+                        "type": "string",
+                        "title": "起始月份",
+                        "description": "YYYY-MM格式",
+                        "format": "YYYY-MM"
+                    },
+                    "end_at": {
+                        "type": "string",
+                        "title": "结束月份",
+                        "description": "YYYY-MM格式",
+                        "format": "YYYY-MM"
+                    }
+                },
+                "x-apifox-orders": [
+                    "begin_at",
+                    "end_at"
+                ],
+                "title": "请求参数内容",
+                "required": [
+                    "begin_at",
+                    "end_at"
+                ]
+            },
+            "ver": {
+                "type": "string",
+                "title": "版本号",
+                "description": "文档中没有说明时,默认为1.0",
+                "examples": [
+                    "1.0"
+                ]
+            },
+            "app_id": {
+                "type": "string",
+                "title": "应用id",
+                "description": "平台分配给客户端的app id。一个应用一个id。"
+            },
+            "timestamp": {
+                "type": "integer",
+                "description": "UTC时间戳,精确到毫秒。平台对超过五分钟的消息,将做忽略处理",
+                "title": "UTC时间戳"
+            },
+            "sign": {
+                "type": "string",
+                "title": "签名"
+            },
+            "token": {
+                "type": "string",
+                "title": "身份认证token",
+                "description": "平台登录后分配的token"
+            }
+        },
+        "x-apifox-orders": [
+            "ver",
+            "app_id",
+            "timestamp",
+            "token",
+            "sign",
+            "data"
+        ],
+        "required": [
+            "data",
+            "ver",
+            "app_id",
+            "timestamp",
+            "token"
+        ]
+    },
+    method: get_list,
+    method_params: {}
+}
+
+
+module.exports = {
+    default: v1_0,
+    v1_0: v1_0
+}

+ 36 - 0
pmr-biz-manager/src/utils/prj_stat.ts

@@ -0,0 +1,36 @@
+import {PrjInfo} from "@core-models/PrjInfo";
+import {Op} from "sequelize";
+import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
+import dayjs from "dayjs";
+import {ReportPrjState} from "@core-models/ReportPrjState";
+
+export  function prj_stat_monthly(): Promise<void> {
+    return new Promise(async (resolve, reject) => {
+        try {
+            // 使用dayjs获取今天是否是月末这一天
+            // let is_last_day = dayjs().isSame(dayjs().endOf('month'), 'day');
+            //if (!is_last_day) return resolve();
+            let count = await PrjInfo.count({where: {phase_id: {[Op.ne]: 'deprecated'}}});
+            let undone = await PrjInfo.count({where: {phase_id: {[Op.ne]: 'done'}}});
+            let ts = dayjs().endOf("month").format('YYYY-MM-DD');
+            // await PrjStatReport.findOrCreate({
+            //     where: {
+            //         ts: ts
+            //     },
+            //     defaults: {
+            //         ts: ts,
+            //         amount: count,
+            //         undone: undone
+            //     }
+            // })
+            await ReportPrjState.upsert({
+                ts: ts,
+                amount: count,
+                undone: undone
+            }, {returning: false})
+            resolve();
+        } catch (error) {
+            reject(error);
+        }
+    });
+}