初始版本,目前线上可用

This commit is contained in:
2025-11-19 12:49:16 +08:00
commit cb7f1c45e8
178 changed files with 30336 additions and 0 deletions

1
backEnd/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

0
backEnd/README.md Normal file
View File

24
backEnd/index.ts Normal file
View File

@@ -0,0 +1,24 @@
require("module-alias/register"); // 别名加载
import express from "express";
import bodyParser from "body-parser";
import routerIndex from "./src/router/index";
import { createConnection } from "typeorm";
import cors from "cors";
const app = express();
const port = 8101;
createConnection().then(() => {
console.log("数据库连接成功!");
app.listen(port, () => {
console.log(`开始监听${port}`);
});
});
app.use(bodyParser.urlencoded({ extended: true })); // 进行url解码
app.use(bodyParser.json());
app.use(cors());
// 匹配接口路由
app.use("/", routerIndex);

17
backEnd/ormconfig.js Normal file
View File

@@ -0,0 +1,17 @@
const env = process.env.NODE_ENV;
module.exports = {
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "123456",
"database": "binyi",
"entities": ["./src/entity/*.ts"],
"migrations": ["src/migration/*.ts"],
"synchronize": true,
"logging": false,
"cli": {
"entitiesDir": "src/entity",
"migrationsDir": "src/migration"
}
}

7472
backEnd/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
backEnd/package.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "interface",
"version": "1.0.0",
"description": "",
"type": "commonjs",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"db": "rd ./src/entity & typeorm-model-generator -h localhost -d baseSystem -p 3306 -u root -x 123456 -e mysql -o ./src/entity --noConfig true --ce pascal --cp camel",
"dev": "nodemon --watch 'src/**/*.ts' --exec ts-node ./index.ts --development",
"build": "webpack "
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/body-parser": "^1.19.2",
"@types/jsonwebtoken": "^9.0.9",
"axios": "^1.3.4",
"bcrypt": "^5.1.0",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"dayjs": "^1.11.10",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"module-alias": "^2.2.3",
"multer": "^1.4.5-lts.1",
"mysql": "^2.18.1",
"reflect-metadata": "^0.1.13",
"ts-loader": "^9.5.1",
"typeorm": "^0.2.9",
"webpack": "^5.95.0"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.17",
"@types/module-alias": "^2.0.4",
"@types/mysql": "^2.15.21",
"cors": "^2.8.5",
"ts-node": "^10.9.1",
"webpack-cli": "^5.1.4"
},
"_moduleAliases": {
"@": "./src",
"@router": "./src/router",
"@entity": "./src/entity"
}
}

View File

@@ -0,0 +1,34 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import dayjs from "dayjs";
export abstract class BaseEntity {
@PrimaryGeneratedColumn({ type: "int", name: "id" })
id: number;
@Column({
type: "datetime",
comment: "创建时间",
default: () => "CURRENT_TIMESTAMP",
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
createDate: Date;
@Column({
type: "datetime",
comment: "更新时间",
default: () => "CURRENT_TIMESTAMP",
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
updateDate: Date;
}

View File

@@ -0,0 +1,79 @@
import { BaseEntity } from "@/abstrClass/BaseEntity";
import { Entity, Column } from "typeorm";
@Entity("cacnle_payment")
export class CancelPayment extends BaseEntity {
@Column({
type: "datetime",
name: "checkout_date",
comment: "结账日期",
default: () => "CURRENT_TIMESTAMP",
})
checkoutDate: Date;
@Column({ type: "varchar", length: 50, comment: "经办人" })
handler: string;
@Column({
type: "datetime",
name: "settlement_date",
comment: "结算日期",
default: () => "CURRENT_TIMESTAMP",
})
settlementDate: Date;
@Column({
type: "decimal",
precision: 12,
scale: 2,
name: "cash_amount",
comment: "现金金额",
})
cashAmount: number;
@Column({
type: "decimal",
precision: 12,
comment: "银联支付金额",
scale: 2,
name: "union_pay_amount",
})
unionPayAmount: number;
@Column({
type: "decimal",
precision: 12,
comment: "刷卡金额",
scale: 2,
name: "card_amount",
})
cardAmount: number;
@Column({
type: "decimal",
precision: 12,
scale: 2,
comment: "对公转账金额",
name: "public_transfer_amount",
})
publicTransferAmount: number;
@Column({
type: "decimal",
precision: 12,
scale: 2,
comment: "车间支付",
name: "workshop_payment",
})
workshopPayment: number;
@Column({
type: "int",
comment: "单子ID",
name: "retail_id",
default: 0,
})
retailId: number;
}
export default CancelPayment;

View File

@@ -0,0 +1,89 @@
import { Entity, Column, OneToOne, JoinColumn } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
import DeceasedRetail from "./DeceasedRetail";
import dayjs from "dayjs";
@Entity("cancel_retail")
export class CancelRetail extends BaseEntity {
@Column("int", {
name: "deceasedRetail_id",
comment: "销售单ID",
default: 0,
})
deceasedRetailId: number;
@Column("int", {
name: "checkoutRetail_id",
comment: "服务销售单ID",
default: 0,
})
checkoutRetailId: number;
@Column("varchar", {
name: "cancel_person",
length: 100,
comment: "作废申请人",
default: "",
})
cancelPerson: string;
@Column("datetime", {
name: "cancel_date",
comment: "作废日期",
default: () => "CURRENT_TIMESTAMP", // 默认值为当前时间
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
cancelDate: Date;
@Column("varchar", {
name: "cancel_reason",
comment: "作废原因",
default: "",
})
cancelReason: string;
@Column("int", {
name: "examine_state",
comment: "审核状态 (0: 未审核, 1: 已审核, 2: 拒绝)",
default: 0,
})
examineState: number;
@Column("int", {
name: "cancel_type",
comment: "作废类型0结账处理、1零售结算",
default: 0,
})
cancelType: number;
@Column("varchar", {
name: "examine_pserson",
comment: "审核人",
default: "",
})
examinePserson: string;
@Column("datetime", {
name: "examine_date",
comment: "审核时间",
nullable: true, // 允许为空
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
examineDate: Date;
}
export default CancelRetail;

View File

@@ -0,0 +1,78 @@
import { BaseEntity } from "@/abstrClass/BaseEntity";
import { Entity, Column } from "typeorm";
@Entity("checkout_payment_records")
export class CheckoutPaymentRecords extends BaseEntity {
@Column({
type: "datetime",
name: "checkout_date",
comment: "结账日期",
default: () => "CURRENT_TIMESTAMP",
})
checkoutDate: Date;
@Column({ type: "varchar", length: 50, comment: "经办人" })
handler: string;
@Column({
type: "datetime",
name: "settlement_date",
comment: "结算日期",
default: () => "CURRENT_TIMESTAMP",
})
settlementDate: Date;
@Column({
type: "decimal",
precision: 12,
scale: 2,
name: "cash_amount",
comment: "现金金额",
})
cashAmount: number;
@Column({
type: "decimal",
precision: 12,
comment: "银联支付金额",
scale: 2,
name: "union_pay_amount",
})
unionPayAmount: number;
@Column({
type: "decimal",
precision: 12,
comment: "刷卡金额",
scale: 2,
name: "card_amount",
})
cardAmount: number;
@Column({
type: "decimal",
precision: 12,
scale: 2,
comment: "对公转账金额",
name: "public_transfer_amount",
})
publicTransferAmount: number;
@Column({
type: "decimal",
precision: 12,
scale: 2,
comment: "车间支付",
name: "workshop_payment",
})
workshopPayment: number;
@Column({
type: "int",
comment: "单子ID",
name: "checkout_retail_id",
})
checkoutRetailId: number;
}
export default CheckoutPaymentRecords;

View File

@@ -0,0 +1,85 @@
import { Entity, Column } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
import dayjs from "dayjs";
@Entity("checkout_retail")
export class CheckoutRetail extends BaseEntity {
@Column("int", {
name: "deceased_id",
comment: "逝者ID",
default: 0,
})
deceasedId: number;
@Column("varchar", {
name: "buyer",
comment: "购买人",
length: 255,
default: "",
})
buyer: string;
@Column("datetime", {
name: "purchase_date",
comment: "购买日期",
default: () => "CURRENT_TIMESTAMP", // 默认值为当前时间
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
purchaseDate: Date;
@Column("varchar", {
name: "handler",
comment: "经办人",
length: 255,
default: "",
})
handler: string;
@Column("decimal", {
name: "sales_amount",
comment: "销售金额",
precision: 10,
scale: 2,
default: 0.0,
})
salesAmount: number;
@Column("varchar", {
name: "guide",
comment: "引导员",
length: 255,
default: "",
})
guide: string;
@Column("varchar", {
name: "service_items",
comment: "服务项目列表",
})
serviceItems: string;
// 0未结账、1已结账
@Column("int", {
name: "retail_state",
comment: "结账状态",
default: 0,
})
retailState: number;
// 0正常、1已作废
@Column("int", {
name: "cancel_state",
comment: "作废状态",
default: 0,
})
cancelState: number;
}
export default CheckoutRetail;

View File

@@ -0,0 +1,117 @@
import { Entity, Column } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
import dayjs from "dayjs";
@Entity("deceased")
export class Deceased extends BaseEntity {
@Column("varchar", {
name: "name",
comment: "逝者姓名",
length: 255,
default: "",
})
name: string;
@Column("varchar", {
name: "id_number",
comment: "证件号码",
length: 50,
default: "",
})
idNumber: string;
@Column("varchar", {
name: "gender",
comment: "性别",
length: 10,
default: "男",
})
gender: string;
@Column("int", {
comment: "年龄",
default: 0,
})
age: number;
@Column("varchar", {
name: "buyer",
comment: "购买人",
length: 255,
default: "",
})
buyer: string;
@Column("varchar", {
name: "family_name",
comment: "购买人",
length: 255,
default: "",
})
familyName: string;
@Column("varchar", {
name: "family_phone",
comment: "购买人电话",
length: 255,
default: 0,
})
familyPhone: string;
@Column({ comment: "所在省", default: "" })
province: string; // 所在省
@Column({ comment: "所在市", default: "" })
city: string; // 所在市
@Column({ comment: "所在区域", default: "" })
area: string; // 所在区域
@Column({ comment: "详细地址", default: "" })
address: string; // 详细地址
@Column("datetime", {
name: "purchase_date",
comment: "购买日期",
default: () => "CURRENT_TIMESTAMP", // 默认值为当前时间
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
purchaseDate: Date;
@Column("varchar", {
name: "handler",
comment: "经办人",
length: 255,
default: "",
})
handler: string;
@Column("decimal", {
name: "sales_amount",
comment: "销售金额",
precision: 10,
scale: 2,
default: 0.0,
})
salesAmount: number;
@Column("varchar", {
name: "guide",
comment: "引导员",
length: 255,
default: "",
})
guide: string;
@Column("simple-array", {
name: "service_items",
comment: "服务项目列表",
})
serviceItems: number[];
}
export default Deceased;

View File

@@ -0,0 +1,143 @@
import { Entity, Column, Index } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
import dayjs from "dayjs";
@Entity("deceased_retail")
export class DeceasedRetail extends BaseEntity {
@Index()
@Column("int", {
name: "deceased_id",
comment: "逝者ID",
default: 0,
})
deceasedId: number;
@Column("varchar", {
name: "deceased_name",
comment: "购买人",
length: 255,
default: "",
})
deceasedName: string;
@Column("varchar", {
name: "buyer",
comment: "购买人",
length: 255,
default: "",
})
buyer: string;
@Column("datetime", {
name: "purchase_date",
comment: "录单日期",
default: () => "CURRENT_TIMESTAMP", // 默认值为当前时间
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
purchaseDate: Date;
@Column("datetime", {
name: "checkout_date",
comment: "录单日期",
default: () => "CURRENT_TIMESTAMP", // 默认值为当前时间
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
checkoutDate: Date;
@Column("varchar", {
name: "handler",
comment: "经办人",
length: 255,
default: "",
})
handler: string;
@Column("decimal", {
name: "sales_amount",
comment: "销售金额",
precision: 10,
scale: 2,
default: 0.0,
})
salesAmount: number;
@Column("varchar", {
name: "guide",
comment: "引导员",
length: 255,
default: "",
})
guide: string;
@Column("varchar", {
name: "service_items",
comment: "服务项目列表",
})
serviceItems: string;
// 0未结账、1已结账
@Column("int", {
name: "retail_state",
comment: "结账状态",
default: 0,
})
retailState: number;
// 0正常、1已作废
@Column("int", {
name: "cancel_state",
comment: "作废状态",
default: 0,
})
cancelState: number;
// 0服务单、1有逝者零售单 2 无逝者零售单
@Column("int", {
name: "retail_type",
comment: "单子类型",
default: 0,
})
retailType: number;
//无逝者零售单
@Column("varchar", {
name: "family_name",
comment: "购买人",
length: 255,
default: "",
})
familyName: string;
@Column("varchar", {
name: "family_phone",
comment: "购买人电话",
length: 255,
default: 0,
})
familyPhone: string;
@Column({ comment: "所在省", default: "" })
province: string; // 所在省
@Column({ comment: "所在市", default: "" })
city: string; // 所在市
@Column({ comment: "所在区域", default: "" })
area: string; // 所在区域
@Column({ comment: "详细地址", default: "" })
address: string; // 详细地址
}
export default DeceasedRetail;

View File

@@ -0,0 +1,102 @@
import { BaseEntity } from "@/abstrClass/BaseEntity";
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from "typeorm";
import DeceasedRetail from "./DeceasedRetail";
@Entity("payment_records")
export class PaymentRecord extends BaseEntity {
@Column({
type: "datetime",
name: "checkout_date",
comment: "结账日期",
default: () => "CURRENT_TIMESTAMP",
})
checkoutDate: Date;
@Column({ type: "varchar", length: 50, comment: "经办人" })
handler: string;
@Column({
type: "datetime",
name: "settlement_date",
comment: "结算日期",
default: () => "CURRENT_TIMESTAMP",
})
settlementDate: Date;
@Column({
type: "decimal",
precision: 12,
scale: 2,
name: "cash_amount",
comment: "现金金额",
})
cashAmount: number;
@Column({
type: "decimal",
precision: 12,
comment: "银联支付金额",
scale: 2,
name: "union_pay_amount",
})
unionPayAmount: number;
@Column({
type: "decimal",
precision: 12,
comment: "刷卡金额",
scale: 2,
name: "card_amount",
})
cardAmount: number;
@Column({
type: "decimal",
precision: 12,
scale: 2,
comment: "对公转账金额",
name: "public_transfer_amount",
})
publicTransferAmount: number;
@Column({
type: "decimal",
precision: 12,
scale: 2,
comment: "车间支付",
name: "workshop_payment",
})
workshopPayment: number;
@Column({
type: "int",
comment: "零售单子ID",
default: 0,
name: "deceased_retail_id",
})
deceasedRetailId: number;
@Column({
type: "int",
comment: "无零售单子ID",
name: "no_deceased_retail_id",
default: 0,
})
noDeceasedRetailId: number;
@Column({
type: "int",
comment: "单子ID",
name: "retail_id",
default: 0,
})
retailId: number;
}
export default PaymentRecord;

View File

@@ -0,0 +1,12 @@
import { Column, Entity } from "typeorm";
import { BaseEntity } from "../abstrClass/BaseEntity";
@Entity("role")
export class Role extends BaseEntity {
@Column({ type: "varchar", comment: "角色值", default: "" })
values: string;
@Column({ type: "int", comment: "角色状态", default: 1 })
roleState: number;
@Column({ type: "varchar", comment: "角色名", default: "" })
name: string;
}

View File

@@ -0,0 +1,53 @@
import { Entity, Column } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
@Entity("seleted_service_list")
export class SeletedServiceList extends BaseEntity {
@Column("varchar", {
name: "name",
comment: "服务项目名称",
length: 255,
default: "",
})
name: string;
@Column("int", {
comment: "数量",
default: 0,
})
quantity: number;
@Column("varchar", {
name: "unit",
comment: "单位",
length: 50,
default: "",
})
unit: string;
@Column("decimal", {
name: "price",
comment: "售价",
precision: 10,
scale: 2,
default: 0.0,
})
price: number;
@Column("varchar", {
name: "remark",
comment: "备注",
length: 500,
default: "",
})
remark: string;
@Column("int", {
name: "retail_id",
comment: "所属零售ID",
default: 0,
})
retailId: number;
}
export default SeletedServiceList;

View File

@@ -0,0 +1,30 @@
import { Entity, Column } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
@Entity("service_category")
export class ServiceCategory extends BaseEntity {
@Column("varchar", {
name: "name",
comment: "商品分类名称",
length: 255,
default: "",
})
name: string; // 商品分类名称
@Column("varchar", {
name: "remark",
comment: "备注",
length: 500,
default: "",
})
remark: string;
@Column("int", {
name: "parentId",
comment: "分类ID",
default: 0,
})
parentId: number; // 关联分类
}
export default ServiceCategory;

View File

@@ -0,0 +1,60 @@
import { Entity, Column } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
@Entity("service_item")
export class ServiceItem extends BaseEntity {
@Column("varchar", {
name: "name",
comment: "服务项目名称",
length: 255,
default: "",
})
name: string;
@Column("int", {
comment: "数量",
default: 0,
})
quantity: number;
@Column("varchar", {
name: "unit",
comment: "单位",
length: 50,
default: "",
})
unit: string;
@Column("decimal", {
name: "price",
comment: "售价",
precision: 10,
scale: 2,
default: 0.0,
})
price: number;
@Column("varchar", {
name: "remark",
comment: "备注",
length: 500,
default: "",
})
remark: string;
@Column("int", {
name: "parentId",
comment: "分类ID",
default: 0,
})
parentId: number; // 关联分类
@Column("int", {
name: "hasDeceased",
comment: "是否有逝者",
default: 1,
})
hasDeceased: number; // 关联分类
}
export default ServiceItem;

View File

@@ -0,0 +1,28 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity("system_menue")
export class SystemMenue {
@PrimaryGeneratedColumn()
id: number;
@Column({ comment: "菜单名", default: "" })
name: string;
@Column({ comment: "创建时间", default: "" })
createDate: string;
@Column({ comment: "更新时间", default: "" })
updateDate: string;
@Column({ comment: "路径名", default: "" })
path: string;
@Column({ comment: "父级ID", default: 0 })
parentId: number;
@Column({ comment: "图标", default: "" })
icon: string;
@Column({ comment: "是否显示", default: true })
show: boolean;
}

View File

@@ -0,0 +1,45 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import dayjs from "dayjs";
import { BaseEntity } from "@/abstrClass/BaseEntity";
@Entity("user")
export class User extends BaseEntity {
@PrimaryGeneratedColumn({ type: "int", name: "id" })
id: number;
@Column("varchar", { name: "name", comment: "姓名\r\n", length: 255 })
name: string;
@Column("varchar", {
name: "phone",
nullable: true,
comment: "电话",
length: 255,
default: "",
})
phone: string;
@Column({ comment: "性别", default: "男" })
sex: string;
@Column("varchar", { name: "pwd", nullable: true, length: 255 })
pwd: string;
@Column({
comment: "创建时间",
default: dayjs().format("YYYY-MM-DD HH:mm:ss"),
})
@Column({ comment: "用户状态", default: 1 })
userState: number; // 账户状态
@Column({ comment: "角色", default: "" })
role: string; // 角色
@Column({ comment: "生日", default: "" })
birthday: string; // 生日
@Column({ comment: "年龄", default: "" })
age: string; // 年龄
@Column({ comment: "所在省", default: "" })
province: string; // 所在省
@Column({ comment: "所在市", default: "" })
city: string; // 所在市
@Column({ comment: "所在区域", default: "" })
area: string; // 所在区域
@Column({ comment: "详细地址", default: "" })
address: string; // 详细地址
}
export default User;

View File

@@ -0,0 +1,94 @@
import { Entity, Column } from "typeorm";
import { BaseEntity } from "@/abstrClass/BaseEntity";
import dayjs from "dayjs";
@Entity("nodeceased_retail")
export class noDeceasedRetail extends BaseEntity {
@Column("varchar", {
name: "buyer",
comment: "购买人",
length: 255,
default: "",
})
buyer: string;
@Column("datetime", {
name: "purchase_date",
comment: "购买日期",
default: () => "CURRENT_TIMESTAMP", // 默认值为当前时间
transformer: {
to(value: Date) {
return value;
},
from(value) {
return dayjs(new Date(value)).format("YYYY-MM-DD HH:mm:ss");
},
},
})
purchaseDate: Date;
@Column("varchar", {
name: "handler",
comment: "经办人",
length: 255,
default: "",
})
handler: string;
@Column("decimal", {
name: "sales_amount",
comment: "销售金额",
precision: 10,
scale: 2,
default: 0.0,
})
salesAmount: number;
@Column("varchar", {
name: "guide",
comment: "引导员",
length: 255,
default: "",
})
guide: string;
@Column("varchar", {
name: "family_name",
comment: "购买人",
length: 255,
default: "",
})
familyName: string;
@Column("varchar", {
name: "family_phone",
comment: "购买人电话",
length: 255,
default: 0,
})
familyPhone: string;
@Column("varchar", {
name: "service_items",
comment: "服务项目列表",
})
serviceItems: string;
// 0未结账、1已结账
@Column("int", {
name: "retail_state",
comment: "结账状态",
default: 0,
})
retailState: number;
// 0正常、1已作废
@Column("int", {
name: "cancel_state",
comment: "作废状态",
default: 0,
})
cancelState: number;
}
export default noDeceasedRetail;

View File

@@ -0,0 +1,101 @@
import { Request } from "express";
import { Repository, getConnection } from "typeorm";
import { filterObjEmptyVal } from "@/util/globalMethods";
import dayjs from "dayjs";
import pagination from "../pagination/pagination";
import { BaseEntity } from "@/abstrClass/BaseEntity";
import getBuilderPagination from "../pagination/builderPagination";
interface CurdOptions<T> {
entity: { new (): T } & typeof BaseEntity; // 确保实体继承 BaseEntity
req: Request;
params?: Partial<T>;
}
class Curd<T extends BaseEntity> {
private entity: { new (): T };
private req: Request;
private queryParams?: { [key: string]: any };
private repositrory: Repository<unknown>;
private params?: Partial<T>;
constructor(options: CurdOptions<T> & { repository?: Repository<T> }) {
if (!options.entity) throw Error("请传入实体类");
if (!options.req)
throw Error("请传express的Request参数一般在中间件回调中的第一个参数");
// if (!options.params) throw Error("请传入需要操作的条件对象用于sql查询");
this.req = options.req;
this.entity = options.entity;
this.repositrory =
options.repository || getConnection().getRepository(options.entity);
this.queryParams =
this.req.method === "GET" ? this.req.query : this.req.body;
this.params = options.params;
}
async add() {
let addData = { ...new this.entity(), ...this.queryParams };
addData.updateDate = new Date();
addData.createDate = new Date();
return await this.repositrory.save(addData);
}
async delete() {
if (!this.queryParams?.id)
return {
code: 500,
msg: "没有传入需要删除数据的ID",
};
let queryData = await this.repositrory.findOne({
where: { id: this.queryParams.id },
});
if (!queryData) return { code: 500, msg: "删除的数据不存在" };
await this.repositrory.remove(queryData);
return {
code: 200,
msg: "删除成功",
};
}
async update() {
let body = this.queryParams;
await this.repositrory.update(
{ id: body.id },
{ ...this.queryParams, updateDate: new Date() }
);
return await this.repositrory.findOne(this.queryParams.id);
}
async query() {
return await this.repositrory.find({
where: filterObjEmptyVal({ ...this.queryParams, ...this.params }),
order: {
createDate: "DESC",
},
});
}
async queryList() {
return await pagination(
this.entity,
this.req,
filterObjEmptyVal({ ...this.queryParams, ...this.params })
);
}
async queryBuilderList() {
return await getBuilderPagination(
this.entity,
this.req,
filterObjEmptyVal({ ...this.queryParams, ...this.params })
);
}
}
export default function curd<T extends BaseEntity>(
options: CurdOptions<T>
): Curd<T> {
return new Curd(options);
}

View File

@@ -0,0 +1,32 @@
import { SystemMenue } from "../entity/SystemMenue";
interface routerTree extends SystemMenue {
children?: SystemMenue[];
}
let filterKey = (obj: SystemMenue) => {
delete obj.id;
delete obj.updateDate;
delete obj.createDate;
delete obj.parentId;
return obj;
};
export default function menueToTree(data: SystemMenue[]): routerTree[] {
let dataList = [];
if (!data.some((item) => item.parentId === 0)) {
return data.map((item) => filterKey(item));
}
for (let i = 0; i < data.length; i++) {
let item: routerTree = data[i];
item.children = [];
for (let j = 0; j < data.length; j++) {
let childrenItem = data[j];
if (childrenItem.parentId === item.id) {
item.children.push(filterKey({ ...childrenItem }));
}
}
// 只添加父级
if (!item.parentId) dataList.push(filterKey({ ...item }));
}
return dataList;
}

View File

@@ -0,0 +1,113 @@
import { getConnection, Between, Brackets } from "typeorm";
import { Request } from "express";
type paginationType<T> = {
list: T[];
total: number;
pageSize: number;
pageNumber: number;
};
const filterKeys = ["pwd"];
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
export default async function getBuilderPagination<T>(
entity: Function,
req: Request,
queryParams?: { [key: string]: any }
): Promise<paginationType<T>> {
try {
// 获取分页参数
const { pageSize, pageNumber } = getPaginationParams(req);
const repository = getConnection().manager.getRepository(entity);
const alias = "entity";
// 创建 QueryBuilder
const queryBuilder = repository.createQueryBuilder(alias);
// 处理查询参数
if (queryParams) {
Object.entries(queryParams).forEach(([key, value]) => {
if (!["pageSize", "pageNumber"].includes(key)) {
// 新增非空条件处理
if (value === "NOT_EMPTY") {
queryBuilder.andWhere(
new Brackets((qb) => {
qb.where(`${alias}.${key} IS NOT NULL`).andWhere(
`${alias}.${key} != ''`
);
})
);
}
// 处理日期范围
else if (typeof value === "string" && DATE_REGEX.test(value)) {
const start = new Date(`${value}T00:00:00`);
const end = new Date(`${value}T23:59:59`);
queryBuilder.andWhere(
new Brackets((qb) =>
qb
.where(`${alias}.${key} >= :start`, { start })
.andWhere(`${alias}.${key} <= :end`, { end })
)
);
}
// 处理普通参数
else if (value !== undefined) {
// 处理数组参数(如?status=1,2,3
if (typeof value === "string" && value.includes(",")) {
queryBuilder.andWhere(`${alias}.${key} IN (:...${key})`, {
[key]: value.split(","),
});
}
// 处理普通值
else {
queryBuilder.andWhere(`${alias}.${key} = :${key}`, {
[key]: value,
});
}
}
}
});
}
// 添加排序
queryBuilder.orderBy(`${alias}.createDate`, "DESC");
// 执行分页查询
const [list, total] = await queryBuilder
.skip((pageNumber - 1) * pageSize)
.take(pageSize)
.getManyAndCount();
// 过滤敏感字段
const filteredList = list.map((item: any) => {
const filtered = { ...item };
filterKeys.forEach((key) => delete filtered[key]);
return filtered;
});
return {
list: filteredList,
total,
pageSize,
pageNumber,
};
} catch (err) {
throw new Error(`分页查询出错: ${err.message}`);
}
}
/**
* 获取分页参数
*/
function getPaginationParams(req: Request): {
pageSize: number;
pageNumber: number;
} {
const { pageSize = 10, pageNumber = 1 } =
req.method === "GET" ? req.query : req.body;
return {
pageSize: Math.max(1, parseInt(pageSize as string, 10)),
pageNumber: Math.max(1, parseInt(pageNumber as string, 10)),
};
}

View File

@@ -0,0 +1,97 @@
import { getConnection } from "typeorm";
import { Request } from "express";
type paginationType<T> = {
list: T[] | any[];
total: number;
pageSize: number;
pageNumber: number;
};
const filterKeys = ["pwd"];
/**
*
* @param entites 传入实体类数组以供查询(支持多表)
* @param req express的Request请求体
* @param queryParams 查询参数
* @param aliasMap 表别名映射,键为实体类名,值为别名
* @returns
*/
export default async function MultPagination<T>(
entites: Function[],
req: Request,
queryParams?: { [key: string]: any },
aliasMap: { [key: string]: string } = {}
): Promise<paginationType<T>> {
const { pageSize, pageNumber } = getPaginationParams(req);
// 获取 QueryBuilder
const queryBuilder = getConnection().createQueryBuilder();
// 构造 FROM 部分,支持多表
entites.forEach((entite, index) => {
const alias = aliasMap[entite.name] || `alias${index}`;
if (index === 0) {
queryBuilder.from(entite, alias); // 主表
} else {
queryBuilder.leftJoinAndSelect(entite, alias); // 关联表
}
});
// 构造 WHERE 条件
if (queryParams) {
Object.entries(queryParams).forEach(([key, value]) => {
queryBuilder.andWhere(`${getWhereKey(key, aliasMap)} = :${key}`, {
[key]: value,
});
});
}
// 构造排序
queryBuilder.orderBy("alias0.createDate", "ASC");
// 分页处理
queryBuilder.skip((pageNumber - 1) * pageSize).take(pageSize);
try {
const [list, total] = await queryBuilder.getManyAndCount();
// 过滤敏感数据
list.forEach((item) => {
filterKeys.forEach((key) => {
delete item[key];
});
});
return {
list,
total,
pageSize,
pageNumber,
};
} catch (err) {
throw new Error("分页查询出错:" + err.message);
}
}
function getPaginationParams(req: Request): {
pageSize: number;
pageNumber: number;
} {
const { pageSize = 10, pageNumber = 1 } =
req.method === "GET" ? req.query : req.body;
const realPageSize = Math.max(1, parseInt(pageSize as string, 10));
const realPageNumber = Math.max(1, parseInt(pageNumber as string, 10));
return { pageSize: realPageSize, pageNumber: realPageNumber };
}
// 获取WHERE条件时自动识别别名
function getWhereKey(key: string, aliasMap: { [key: string]: string }) {
for (const alias in aliasMap) {
if (key.startsWith(alias + ".")) {
return key; // 已包含别名
}
}
return `alias0.${key}`; // 默认主表
}

View File

@@ -0,0 +1,83 @@
import { getConnection, Between } from "typeorm";
import { Request } from "express";
type paginationType<T> = {
list: T[] | any[];
total: number;
pageSize: number;
pageNumber: number;
};
const filterKeys = ["pwd"];
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
/**
* 处理日期查询参数
*/
function processDateParams(params: { [key: string]: any }): {
[key: string]: any;
} {
const processed = {} as Record<string, any>;
for (const [key, value] of Object.entries(params)) {
if (typeof value === "string" && DATE_REGEX.test(value)) {
// 处理日期范围
const start = new Date(`${value}T00:00:00`);
const end = new Date(`${value}T23:59:59`);
processed[key] = Between(start, end);
} else {
processed[key] = value;
}
}
return processed;
}
/**
* 获取分页参数
*/
function getPaginationParams(req: Request): {
pageSize: number;
pageNumber: number;
} {
const { pageSize = 10, pageNumber = 1 } =
req.method === "GET" ? req.query : req.body;
return {
pageSize: Math.max(1, parseInt(pageSize as string, 10)),
pageNumber: Math.max(1, parseInt(pageNumber as string, 10)),
};
}
/**
* 分页查询封装
*/
export default async function getPagination<T>(
entity: Function,
req: Request,
queryParams?: { [key: string]: any }
): Promise<paginationType<T>> {
try {
const { pageSize, pageNumber } = getPaginationParams(req);
const repository = getConnection().manager.getRepository(entity);
// 处理日期查询参数
const processedWhere = queryParams ? processDateParams(queryParams) : {};
const [list, total] = await repository.findAndCount({
where: processedWhere,
order: { createDate: "DESC" },
skip: (pageNumber - 1) * pageSize,
take: pageSize,
});
// 过滤敏感字段
list.forEach((item) => {
filterKeys.forEach((key) => delete item[key]);
});
return { list, total, pageSize, pageNumber };
} catch (err) {
throw new Error(`分页查询出错: ${err.message}`);
}
}

View File

@@ -0,0 +1,220 @@
import * as express from "express";
import { Brackets, getConnection } from "typeorm";
import Deceased from "@/entity/Deceased";
import CheckoutPayment from "@/entity/CheckoutPayment";
import DeceasedRetail from "@/entity/DeceasedRetail";
import { Request, Response } from "express";
import parseRangDate, {
filterObjEmptyVal,
getPaginationParams,
} from "@/util/globalMethods";
import dayjs from "dayjs";
const router = express.Router();
// 统一查询方法
async function queryRetailRecords(params: {
req: Request;
retailState?: number;
isQuery?: boolean;
}): Promise<{
list: any[];
total: number;
pageSize: number;
pageNumber: number;
}> {
const { req, retailState } = params;
const { pageSize, pageNumber } = getPaginationParams(req);
const alias = "r";
const paymentAlias = "p";
const deceasedAlias = "d";
let { startDate, endDate } = parseRangDate(req);
// 初始化查询构造器
const queryBuilder = getConnection()
.getRepository(DeceasedRetail)
.createQueryBuilder(alias)
.leftJoinAndMapOne(
`${alias}.paymentRecord`,
CheckoutPayment,
paymentAlias,
`${paymentAlias}.checkout_retail_id = ${alias}.id`
)
.leftJoinAndMapOne(
`${alias}.deceased`,
Deceased,
deceasedAlias,
`${deceasedAlias}.id = ${alias}.deceasedId`
)
.where(`${alias}.retailType = 0`)
.andWhere(`${alias}.cancelState = 0`);
// 零售状态筛选
if (typeof retailState === "number") {
queryBuilder.andWhere(`${alias}.retailState = :retailState`, {
retailState,
});
}
queryBuilder.andWhere(
new Brackets((qb) =>
qb
.where(`${alias}.serviceItems IS NOT NULL`)
.andWhere(`${alias}.serviceItems != ''`)
)
);
if (startDate && endDate) {
queryBuilder
.andWhere(`p.checkoutDate BETWEEN :start AND :end`)
.setParameters({ start: startDate, end: endDate });
}
// 动态处理查询参数
const queryParams: any = filterObjEmptyVal(
req.method === "GET" ? req.query : req.body
);
Object.entries(queryParams).forEach(([key, value]) => {
if (["pageSize", "pageNumber"].includes(key)) return;
if (value === undefined || value === "") return;
switch (key) {
// case "checkoutDate":
// queryBuilder.andWhere(`${paymentAlias}.checkoutDate = :checkoutDate`, {
// checkoutDate: value,
// });
// break;
case "gender":
queryBuilder.andWhere(`${deceasedAlias}.gender = :gender`, {
gender: value,
});
break;
case "name":
queryBuilder.andWhere(`${deceasedAlias}.name LIKE :name`, {
name: `%${value}%`,
});
break;
default:
if (key.startsWith("deceased.")) {
const field = key.split(".")[1];
queryBuilder.andWhere(`${deceasedAlias}.${field} = :${field}`, {
[field]: value,
});
} else if (
Object.keys(queryBuilder.expressionMap.parameters).includes(key)
) {
// 处理重复参数名
const uniqueKey = `${key}_${Date.now()}`;
queryBuilder.andWhere(`${alias}.${key} = :${uniqueKey}`, {
[uniqueKey]: value,
});
} else {
if (!["purchaseDate", "startDate", "endDate"].includes(key)) {
queryBuilder.andWhere(`${alias}.${key} = :${key}`, {
[key]: value,
});
}
}
}
});
// 执行分页查询
const [list, total] = await queryBuilder
.orderBy(`${alias}.createDate`, "DESC")
.skip((pageNumber - 1) * pageSize)
.take(pageSize)
.getManyAndCount();
return { list, total, pageSize, pageNumber };
}
// 列表接口
router.get("/list", async (req: Request, res: Response) => {
try {
const result = await queryRetailRecords({
req,
retailState: Number(req.query.retailState),
});
res.json({
code: 200,
data: {
list: result.list,
pageNumber: result.pageNumber,
pageSize: result.pageSize,
total: result.total,
},
});
} catch (err) {
res.status(500).json({
code: 500,
msg: err.message,
});
}
});
// 查询接口
router.post("/query", async (req: Request, res: Response) => {
try {
const result = await queryRetailRecords({
req,
isQuery: true,
});
res.json({
code: 200,
data: {
list: result.list,
pageNumber: result.pageNumber,
pageSize: result.pageSize,
total: result.total,
},
});
} catch (err) {
res.status(500).json({
code: 500,
msg: err.message,
});
}
});
router.post("/confirmCheckout", async (req, res) => {
let id = Number(req.body.id);
if (!id) {
return res.status(500).send({ code: 500, msg: "未传入结账id" });
}
try {
let connection = getConnection();
let deceasedRetailRep = connection.getRepository(DeceasedRetail);
let paymentRecordRep = connection.getRepository(CheckoutPayment);
let deceasedRetail = await deceasedRetailRep.findOne(id);
if (!deceasedRetail) {
return res.status(500).send({ code: 500, msg: "该记录不存在" });
}
let newPaymentRecord = new CheckoutPayment();
deceasedRetail.retailState = 1;
deceasedRetail.checkoutDate = new Date();
newPaymentRecord = Object.assign({}, req.body.currentPayment);
newPaymentRecord.checkoutRetailId = deceasedRetail.id;
await deceasedRetailRep.save(deceasedRetail);
// await cancelRecordRep.save(newCancleRetail);
await paymentRecordRep.save(newPaymentRecord);
res
.status(200)
.send({ code: 200, data: deceasedRetail, msg: "结账成功!" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,328 @@
import * as express from "express";
import { getConnection } from "typeorm";
import curd from "@/lib/curd/curd";
import DeceasedRetail from "@/entity/DeceasedRetail";
import CancelRetail from "@/entity/CancelRetail";
import Deceased from "@/entity/Deceased";
import PaymentRecord from "@/entity/Payment";
import CheckoutRetail from "@/entity/CheckoutRetail";
import CheckoutPaymentRecords from "@/entity/CheckoutPayment";
import CancelPayment from "@/entity/CancelPayment";
const router = express.Router();
async function getMoreInfor(result, type = 0) {
const deceasedRetailIds = result.list
.filter((item) => item.deceasedRetailId)
.map((item) => Number(item.deceasedRetailId));
const checkoutRetailIds = result.list
.filter((item) => item.checkoutRetailId)
.map((item) => Number(item.checkoutRetailId));
if (deceasedRetailIds.length) {
const deceasedRetails = await getConnection()
.getRepository(DeceasedRetail)
.createQueryBuilder("deceased_retail")
.where("deceased_retail.id IN (:...ids)", {
ids: deceasedRetailIds,
})
.getMany();
const deceasedIds = Array.from(
new Set(deceasedRetails.map((item) => Number(item.deceasedId)))
);
let deceaseds = [];
if (deceasedIds.length) {
deceaseds = await getConnection()
.getRepository(Deceased)
.createQueryBuilder("deceased")
.where("deceased.id IN (:...ids)", {
ids: deceasedIds,
})
.getMany();
}
let paymentRecords = [];
if (type === 0) {
paymentRecords = await getConnection()
.getRepository(CheckoutPaymentRecords)
.createQueryBuilder("paymentRecord")
.where("paymentRecord.checkout_retail_id IN (:...ids)", {
ids: deceasedRetailIds,
})
.getMany();
let tempRec = await getConnection()
.getRepository(PaymentRecord)
.createQueryBuilder("paymentRecord")
.where("paymentRecord.deceased_retail_id IN (:...ids)", {
ids: deceasedRetailIds,
})
.getMany();
paymentRecords = [...paymentRecords, ...tempRec];
} else {
let retailIds = result.list.map((item) => Number(item.id));
paymentRecords = await getConnection()
.getRepository(CancelPayment)
.createQueryBuilder("paymentRecord")
.where("paymentRecord.retail_id IN (:...ids)", {
ids: retailIds,
})
.getMany();
}
result.list = result.list.map((item: any) => {
let retail = deceasedRetails.find(
(fitem: any) => fitem.id === item.deceasedRetailId
);
let deceased = {};
if (retail) {
deceased = deceaseds.find((fitem) => fitem.id === retail.deceasedId);
}
let payment =
paymentRecords.find(
(fitem) => fitem.checkoutRetailId === item.deceasedRetailId
) ||
paymentRecords.find((fitem) => fitem.retailId === item.id) ||
paymentRecords.find(
(fitem) => fitem.deceasedRetailId === item.deceasedRetailId
);
return {
retail,
deceased,
payment,
...item,
};
});
}
if (checkoutRetailIds.length) {
const checkouitRetails = await getConnection()
.getRepository(CheckoutRetail)
.createQueryBuilder("checkout_retail")
.where("checkout_retail.id IN (:...ids)", {
ids: checkoutRetailIds,
})
.getMany();
let deceaseds = [];
if (checkouitRetails.length) {
const checkoutRetailsIds = Array.from(
new Set(checkouitRetails.map((item) => Number(item.deceasedId)))
);
if (checkoutRetailsIds.length) {
deceaseds = await getConnection()
.getRepository(Deceased)
.createQueryBuilder("deceased")
.where("deceased.id IN (:...ids)", {
ids: checkoutRetailsIds,
})
.getMany();
}
const paymentRecords = await getConnection()
.getRepository(CheckoutPaymentRecords)
.createQueryBuilder("paymentRecord")
.where("paymentRecord.checkoutRetailId IN (:...ids)", {
ids: checkoutRetailIds,
})
.getMany();
result.list = result.list.map((item: any) => {
let retail = checkouitRetails.find(
(fitem: any) => fitem.id === item.checkoutRetailId
);
let deceased = deceaseds.find(
(fitem) => fitem.id === retail.deceasedId
);
let payment = paymentRecords.find(
(fitem) => fitem.checkoutRetailId === item.checkoutRetailId
);
return {
...item,
retail,
deceased,
payment,
};
});
}
}
}
router.get("/list", async (req, res) => {
try {
const result: any = await curd({
entity: CancelRetail,
req,
params: {
examineState: 0,
},
}).queryList();
// console.log(result);
await getMoreInfor(result);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 查询逝者列表(支持分页)
router.post("/query", async (req, res) => {
try {
const result = await curd({
entity: CancelRetail,
req,
params: {
...req.body,
},
}).queryList();
await getMoreInfor(result, 1);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.post("/cancel", async (req, res) => {
try {
const connection = getConnection();
const deceaseRetailRep = connection.getRepository(DeceasedRetail);
const cancelRetailRep = connection.getRepository(CancelRetail);
let findDeceaseRetail = await deceaseRetailRep.findOne(
Number(req.body.deceasedRetailId)
);
let cancelRetail = new CancelRetail();
findDeceaseRetail.cancelState = 1;
cancelRetail = {
...cancelRetail,
...req.body.cancelForm,
};
// 结账处理
cancelRetail.deceasedRetailId = findDeceaseRetail.id;
cancelRetail.cancelDate = new Date();
await deceaseRetailRep.save(findDeceaseRetail);
await cancelRetailRep.save(cancelRetail);
res.send({ code: 200, data: cancelRetail });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.post("/examine", async (req, res) => {
let examineState = req.body.examineState;
let id = req.body.id;
let connection = getConnection();
if (examineState === 1) {
try {
let cancleRetailRep = connection.getRepository(CancelRetail);
let cancleRetail = await cancleRetailRep.findOne(id);
let cancelPaymentRep = connection.getRepository(CancelPayment);
let cancelPayment = new CancelPayment();
cancelPayment = { ...req.body.payment };
delete cancelPayment.id;
cancelPayment.retailId = cancleRetail.id;
await cancelPaymentRep.save(cancelPayment);
cancleRetail.examineState = 1;
cancleRetail.examineDate = new Date();
if (req.body.checkoutRetailId) {
let checkoutRetailRep = connection.getRepository(CheckoutRetail);
let checkoutRetail = await checkoutRetailRep.findOne(
req.body.checkoutRetailId
);
checkoutRetail.cancelState = 1;
await checkoutRetailRep.save(checkoutRetail);
}
if (req.body.deceasedRetailId) {
let deceasedRetailRep = connection.getRepository(DeceasedRetail);
let deceasedRetail = await deceasedRetailRep.findOne(
req.body.deceasedRetailId
);
deceasedRetail.cancelState = 0;
deceasedRetail.retailState = 0;
await deceasedRetailRep.save(deceasedRetail);
}
await cancleRetailRep.save(cancleRetail);
let paymentRep = connection.getRepository(PaymentRecord);
let findPaymeny = await paymentRep.findOne(req.body.payment.id);
if (findPaymeny) {
findPaymeny.cashAmount = 0;
findPaymeny.unionPayAmount = 0;
findPaymeny.cardAmount = 0;
findPaymeny.publicTransferAmount = 0;
findPaymeny.workshopPayment = 0;
await paymentRep.save(findPaymeny);
}
res.send({ code: 200, data: cancleRetail });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
}
if (examineState === 2) {
try {
let cancleRetailRep = getConnection().getRepository(CancelRetail);
let cancleRetail = await cancleRetailRep.findOne(id);
if (req.body.checkoutRetailId) {
let checkoutRetailRep = getConnection().getRepository(CheckoutRetail);
let checkoutRetail = await checkoutRetailRep.findOne(
req.body.checkoutRetailId
);
checkoutRetail.cancelState = 1;
await checkoutRetailRep.save(checkoutRetail);
}
if (req.body.deceasedRetailId) {
let deceasedRetailRep = getConnection().getRepository(DeceasedRetail);
let deceasedRetail = await deceasedRetailRep.findOne(
req.body.deceasedRetailId
);
deceasedRetail.cancelState = 0;
deceasedRetail.retailState = 1;
await deceasedRetailRep.save(deceasedRetail);
}
await cancleRetailRep.remove(cancleRetail);
res.send({ code: 200, data: cancleRetail });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
}
});
export default router;

View File

@@ -0,0 +1,50 @@
import * as express from "express";
import { getConnection } from "typeorm";
import DeceasedRetail from "@/entity/DeceasedRetail";
import PaymentRecord from "@/entity/Payment";
const router = express.Router();
router.post("/deceasedCheckout", async (req, res) => {
let id = Number(req.body.id);
if (!id) {
return res.status(500).send({ code: 500, msg: "未传入结账id" });
}
try {
let connection = getConnection();
let deceasedRetailRep = connection.getRepository(DeceasedRetail);
let paymentRecordRep = connection.getRepository(PaymentRecord);
let deceasedRetail = await deceasedRetailRep.findOne(id);
if (!deceasedRetail)
return res.status(500).send({ code: 500, msg: "该记录不存在" });
let newPaymentRecord = await paymentRecordRep.findOne({
deceasedRetailId: deceasedRetail.id,
});
if (!newPaymentRecord) {
newPaymentRecord = new PaymentRecord();
}
deceasedRetail.retailState = 1;
deceasedRetail.checkoutDate = new Date();
newPaymentRecord = Object.assign(newPaymentRecord, req.body.currentPayment);
newPaymentRecord.deceasedRetailId = deceasedRetail.id;
await deceasedRetailRep.save(deceasedRetail);
await paymentRecordRep.save(newPaymentRecord);
res
.status(200)
.send({ code: 200, data: deceasedRetail, msg: "结账成功!" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,201 @@
import * as express from "express";
import { getConnection } from "typeorm";
import Deceased from "@/entity/Deceased";
import curd from "@/lib/curd/curd";
import DeceasedRetail from "@/entity/DeceasedRetail";
import { SeletedServiceList } from "@/entity/SeletedServiceList";
import { Request } from "express-serve-static-core";
import PaymentRecord from "@/entity/Payment";
const router = express.Router();
// 查询逝者列表(支持分页)
router.post("/query", async (req, res) => {
try {
const result = await curd({ entity: Deceased, req }).queryList();
await getData(result, req);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.get("/list", async (req, res) => {
try {
const result = await curd({ entity: Deceased, req }).queryList();
await getData(result, req);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 添加逝者
router.post("/add", async (req, res) => {
const { name, idNumber } = req.body;
if (!name || !idNumber) {
return res.send({ code: 400, msg: "逝者姓名和证件号码不能为空!" });
}
try {
const connection = getConnection();
const deceasedRepository = connection.getRepository(Deceased);
const deceasedRetailRep = connection.getRepository(DeceasedRetail);
const tempServiceItems = req.body.services.map((item) => {
delete item.id;
return {
...item,
};
});
req.body.serviceItems = "";
// 校验是否已存在相同证件号码的逝者
const existingDeceased = await deceasedRepository.findOne({
where: { idNumber },
});
if (existingDeceased) {
return res.send({ code: 400, msg: "证件号码已存在,请勿重复添加!" });
}
const newDeceased = await curd({ entity: Deceased, req }).add();
let newDeceasedRetail = await curd({ entity: DeceasedRetail, req }).add();
if (tempServiceItems.length) {
tempServiceItems.forEach((item) => {
item.retailId = newDeceasedRetail.id;
delete item.id;
});
const data = await connection
.getRepository(SeletedServiceList)
.save(tempServiceItems);
newDeceasedRetail.serviceItems = data.map((item) => item.id).join(",");
}
newDeceasedRetail.deceasedId = newDeceased.id;
await deceasedRetailRep.save(newDeceasedRetail);
res.send({ code: 200, msg: "逝者信息添加成功", data: newDeceased });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 更新逝者信息
router.post("/update", async (req, res) => {
const { id, idNumber } = req.body;
if (!id) {
return res.send({ code: 400, msg: "逝者ID不能为空" });
}
try {
const connection = getConnection();
const deceasedRepository = connection.getRepository(Deceased);
// 校验是否已存在相同证件号码的逝者(排除当前记录)
if (idNumber) {
const existingDeceased = await deceasedRepository.findOne({
where: { idNumber },
});
if (existingDeceased && existingDeceased.id !== id) {
return res.send({ code: 400, msg: "证件号码已存在,请勿重复使用!" });
}
}
const updatedDeceased = await curd({ entity: Deceased, req }).update();
res.send({ code: 200, msg: "逝者信息更新成功", data: updatedDeceased });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 删除逝者信息
router.get("/delete", async (req, res) => {
const { id } = req.query;
if (!id) {
return res.send({ code: 400, msg: "逝者ID不能为空" });
}
try {
await curd({ entity: Deceased, req }).delete();
res.send({ code: 200, msg: "逝者信息删除成功" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
async function getData(result, req: Request) {
let type = req.query.type ?? req.body.type;
if (!type && type !== 0) type = 0;
const deceasedId = Array.from(
new Set(result.list.map((item) => Number(item.deceasedId)))
);
const deceasedIds = Array.from(
new Set(result.list.map((item) => Number(item.id)))
);
if (deceasedId.length) {
const reatialList = await getConnection()
.getRepository(DeceasedRetail)
.createQueryBuilder("DeceasedRetail")
.where("DeceasedRetail.deceased_id IN (:...ids)", {
ids: deceasedIds,
})
.andWhere("DeceasedRetail.retail_type = :retailType", {
retailType: type,
})
.getMany();
if (reatialList.length) {
result.list = result.list.map((item: any) => {
return {
...item,
deceased: { ...item },
retail: reatialList.find(
(fitem: any) => fitem.deceasedId === item.id
),
};
});
}
let retailIds = reatialList.map((item) => item.id);
if (retailIds.length) {
const serviceList = await getConnection()
.getRepository(SeletedServiceList)
.createQueryBuilder("SeletedServiceList")
.where("SeletedServiceList.retail_id IN (:...retailIds)", {
retailIds: retailIds,
})
.getMany();
if (serviceList.length) {
result.list = result.list.map((item: any) => {
let retailId = reatialList.find(
(fitem) => fitem.deceasedId === item.id
)?.id;
let services = serviceList.filter(
(fitem) => fitem.retailId === retailId
);
return {
...item,
serviceItems: services.map((mitem) => mitem.id.toString()),
services: services,
};
});
}
}
}
}
export default router;

View File

@@ -0,0 +1,484 @@
import * as express from "express";
import { getConnection } from "typeorm";
import DeceasedRetail from "../../entity/DeceasedRetail";
import curd from "@/lib/curd/curd";
import SeletedServiceList from "@/entity/SeletedServiceList";
import Deceased from "@/entity/Deceased";
import PaymentRecord from "@/entity/Payment";
import parseRangDate, { getPaginationParams } from "@/util/globalMethods";
import { Request, Response } from "express";
const router = express.Router();
async function GetData(result) {
const ids = Array.from(
new Set(result.list.map((item) => Number(item.deceasedId)))
);
if (ids.length) {
const manyResult = await getConnection()
.getRepository(Deceased)
.createQueryBuilder("deceased")
.where("deceased.id IN (:...ids)", {
ids,
})
.getMany();
result.list = result.list.map((item: any) => {
let findData = manyResult.find(
(fitem: any) => fitem.id === item.deceasedId
);
return {
...item,
deceased: findData,
};
});
}
let paymentIds = result.list
.filter((item) => item.retailState === 1)
.map((item) => item.id);
if (paymentIds.length) {
let manyResult = await getConnection()
.getRepository(PaymentRecord)
.createQueryBuilder("PaymentRecord")
.where("PaymentRecord.deceased_retail_id IN (:...ids)", {
ids: paymentIds,
})
.getMany();
result.list = result.list.map((item: any) => {
return {
...item,
payment: manyResult.find((fitem) => fitem.deceasedRetailId === item.id),
};
});
}
}
router.post("/query", async (req: Request, res: Response) => {
try {
const {
pageNumber = 1,
pageSize = 10,
deceased = {},
retail = {},
} = req.body;
// 参数校验
const connection = getConnection();
const queryBuilder = connection
.getRepository(DeceasedRetail)
.createQueryBuilder("retail")
.leftJoinAndMapOne(
"retail.deceased",
Deceased,
"deceased",
"deceased.id = retail.deceased_id"
)
.leftJoinAndMapOne(
"retail.payment",
PaymentRecord,
"payment",
"payment.deceased_retail_id = retail.id"
)
.where(`retail.cancel_state = 0`)
.andWhere("retail.retail_type = :retailType", {
retailType: retail.retailType ?? 0,
});
req.body.startDate = retail.startDate;
req.body.endDate = retail.endDate;
let { startDate, endDate } = parseRangDate(req);
if (retail.retailType === 2) {
if (retail.familyName) {
queryBuilder.andWhere("retail.familyName LIKE :familyName", {
familyName: `%${retail.familyName}%`,
});
}
if (deceased.name) {
queryBuilder.andWhere("retail.deceased_Name LIKE :name", {
name: `%${deceased.name}%`,
});
}
}
if (retail.retailType === 1) {
if (deceased.name) {
queryBuilder.andWhere("deceased.name LIKE :name", {
name: `%${deceased.name}%`,
});
}
if (retail.familyName) {
queryBuilder.andWhere("retail.familyName LIKE :familyName", {
familyName: `%${retail.familyName}%`,
});
}
}
if (startDate && endDate) {
queryBuilder
.andWhere(`retail.purchaseDate BETWEEN :start AND :end`)
.setParameters({ start: startDate, end: endDate });
}
if (retail.guide) {
queryBuilder.andWhere(`retail.guide LIKE :guide`, {
guide: retail.guide,
});
}
// 执行分页查询
const [list, total] = await queryBuilder
.orderBy("retail.createDate", "DESC")
.skip((pageNumber - 1) * pageSize)
.take(pageSize)
.getManyAndCount();
let result = {
list,
total,
pageSize,
pageNumber,
};
await GetData(result);
res.json({
code: 200,
data: result,
});
} catch (err) {
console.error("查询失败:", err);
res.status(500).json({
code: 500,
msg: process.env.NODE_ENV === "production" ? "服务器错误" : err.message,
});
}
});
router.get("/list", async (req, res) => {
try {
let { retailType } = req.method === "GET" ? req.query : req.body;
let queryBuilder = getConnection()
.getRepository(DeceasedRetail)
.createQueryBuilder("dr")
.where(`dr.retailType=${retailType}`)
.andWhere(`dr.cancelState = 0`);
const { pageSize, pageNumber } = getPaginationParams(req);
let [list, total] = await queryBuilder
.orderBy("dr.createDate", "DESC")
.skip((pageNumber - 1) * pageSize)
.take(pageSize)
.getManyAndCount();
let result = {
list,
pageNumber,
pageSize,
total,
};
await GetData(result);
res.status(200).send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
/**
* retailId: 销售单ID
* deceasedId 人的ID且必须传retailType
*/
router.get("/selected-service", async (req, res) => {
let retailId = req.query.retailId;
let deceasedId = req.query.deceasedId;
let retailType = req.query.retailType;
try {
let retail;
if (retailId) {
if (retailType && Number(retailType) === 2) {
retail = await getConnection()
.getRepository(DeceasedRetail)
.findOne({
where: {
id: Number(retailId),
retailType,
},
});
} else {
retail = await getConnection()
.getRepository(DeceasedRetail)
.findOne(Number(retailId));
}
}
if (deceasedId) {
retail = await getConnection()
.getRepository(DeceasedRetail)
.createQueryBuilder("deceasedRetail")
.where("deceasedRetail.retail_type = :retailType", {
retailType,
})
.andWhere("deceasedRetail.deceased_id = :deceasedId", {
deceasedId,
})
.getOne();
}
if (!retail) return res.status(200).send({ code: 200, data: { list: [] } });
const ids = retail.serviceItems
? retail.serviceItems.split(",").map((id) => Number(id))
: [];
if (!ids.length)
return res.status(200).send({ code: 200, data: { list: [] } });
const result = await getConnection()
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.getMany();
res.status(200).send({ code: 200, data: { list: result } });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.get("/checkout", async (req, res) => {
let { id } = req.query;
if (!id) {
return res.status(500).send({ code: 500, msg: "未传入结账id" });
}
let deceasedRetailRep = getConnection().getRepository(DeceasedRetail);
let deceasedRetail = await deceasedRetailRep.findOne(Number(id));
if (!deceasedRetail)
return res.status(500).send({ code: 500, msg: "该记录不存在" });
deceasedRetail.retailState = 1;
await deceasedRetailRep.save(deceasedRetail);
res.status(200).send({ code: 200, data: deceasedRetail, msg: "结账成功!" });
});
// 添加逝者零售
router.post("/add", async (req, res) => {
delete req.body.id;
const connection = getConnection();
try {
const tempServiceItems = req.body.services.map((item) => {
delete item.id;
return item;
});
req.body.serviceItems = "";
const deceasedRetailRep = connection.getRepository(DeceasedRetail);
const newDeceasedRetail = await curd({ entity: DeceasedRetail, req }).add();
if (tempServiceItems.length) {
tempServiceItems.forEach((item) => {
item.retailId = newDeceasedRetail.id;
});
let executeData = await connection
.getRepository(SeletedServiceList)
.save(tempServiceItems);
newDeceasedRetail.serviceItems = executeData
.map((item) => item.id)
.join(",");
await deceasedRetailRep.save(newDeceasedRetail);
}
res.send({ code: 200, msg: "逝者零售添加成功", data: newDeceasedRetail });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.post("/updateRetail", async (req, res) => {
const connection = getConnection();
try {
const tempServiceItems = req.body.services.map((item) => {
return item;
});
req.body.serviceItems = "";
const deceasedRetailRep = connection.getRepository(DeceasedRetail);
let newDeceasedRetail = await deceasedRetailRep.findOne({
id: req.body?.retail?.id,
});
if (!newDeceasedRetail) {
newDeceasedRetail = await deceasedRetailRep.findOne({
where: { deceasedId: req.body.deceasedId, retailType: 0 },
});
}
if (tempServiceItems.length) {
tempServiceItems.forEach((item) => {
item.retailId = newDeceasedRetail.id;
item.updateDate = new Date();
item.createDate = new Date();
});
const ids = newDeceasedRetail.serviceItems
.split(",")
.map((id) => Number(id));
if (ids.length) {
await connection
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.delete()
.execute();
}
let executeData = await connection
.getRepository(SeletedServiceList)
.save(tempServiceItems);
newDeceasedRetail.salesAmount = req.body.salesAmount;
newDeceasedRetail.serviceItems = executeData
.map((item) => item.id)
.join(",");
// 保存人
let deceased = await connection
.getRepository(Deceased)
.findOne(req.body.id);
if (deceased) {
deceased = { ...deceased, ...req.body.deceased };
deceased.salesAmount = newDeceasedRetail.salesAmount;
await connection.getRepository(Deceased).save(deceased);
}
} else {
const ids = newDeceasedRetail.serviceItems
.split(",")
.map((id) => Number(id));
if (ids.length) {
await connection
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.delete()
.execute();
}
newDeceasedRetail.serviceItems = "";
}
await deceasedRetailRep.save(newDeceasedRetail);
res.send({ code: 200, msg: "逝者服务单修改成功", data: newDeceasedRetail });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 更新逝者信息
router.post("/update", async (req, res) => {
const id = Number(req.body.id);
if (!id) {
return res.send({ code: 400, msg: "逝者ID不能为空" });
}
try {
const connection = getConnection();
const deceasedRepository = connection.getRepository(DeceasedRetail);
const deceasedRep = connection.getRepository(Deceased);
let services = req.body.retail?.services || req.body.services;
const tempServiceItems = services.map((item) => {
delete item.id;
return item;
});
let findData = await deceasedRepository.findOne(id);
let decease = await deceasedRep.findOne(Number(findData.deceasedId));
const ids = findData.serviceItems.split(",").map((id) => Number(id));
if (ids.length) {
await connection
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.delete()
.execute();
}
findData = { ...findData, ...req.body };
if (tempServiceItems.length) {
tempServiceItems.forEach((item) => {
item.retailId = id;
item.hasDeceased = 0;
item.updateDate = new Date();
item.createDate = new Date();
});
let executeData = await connection
.getRepository(SeletedServiceList)
.save(tempServiceItems);
findData.serviceItems = executeData.map((item) => item.id).join(",");
}
let newDeceased: Deceased = { ...req.body };
delete newDeceased.id;
decease = { ...decease, ...newDeceased };
await deceasedRep.save(decease);
await deceasedRepository.save(findData);
res.send({ code: 200, msg: "更新成功", data: findData });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 删除逝者信息
router.get("/delete", async (req, res) => {
const id = Number(req.query.id);
if (!id) {
return res.send({ code: 400, msg: "数据ID不能为空" });
}
try {
const connection = getConnection();
const deceasedRepository = connection.getRepository(DeceasedRetail);
const findData = await deceasedRepository.findOne(id);
const ids = findData.serviceItems.split(",").map((id) => Number(id));
if (ids.length) {
await connection
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.delete()
.execute();
}
await deceasedRepository.remove(findData);
res.send({ code: 200, msg: "删除成功!" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,75 @@
import * as express from "express";
import login from "./login/login";
import user from "./user/user";
import systemMenue from "./system-menue/system-menue";
import role from "./role/role";
import deceased from "./deceased/deceased";
import serviceItem from "./serviceList/serviceItem";
import serviceCategory from "./serviceList/serviceCategory";
import deceasedRetail from "./deceasedRetail/deceasedRetail";
import checkout from "./checkout/checkout";
import retailCheckout from "./retailCheckout/retailCheckout";
import noDeceasedRetail from "./noDeceasedRetail/noDeceasedRetail";
import cancleExamine from "./cancle/cancleExamine";
import CheckoutRetail from "./CheckoutRetail/CheckoutRetail";
import publicList from "./public/public";
import Stats from "./stats/stats";
let jwt = require("jsonwebtoken");
let router = express.Router();
const secretKey = "myNameIsLiJiaTu";
// 统一请求入口
router.use("/", (req, res, next) => {
const token = req.headers.authorization;
const refreshToken = req.headers.refreshtoken;
// 非登录并且无token证明用户未登录禁止操作不需要这个注释即可
if (!token && !refreshToken && req.path !== "/login")
return res.send({ code: 401, msg: "未登录,禁止操作访问!" });
if (token) {
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
if (!refreshToken) {
return res.status(401).send({ code: 401, msg: "请重新登录" });
}
jwt.verify(refreshToken, secretKey, (refErr, refDecoded) => {
if (refErr) {
return res
.status(401)
.send({ code: 401, msg: "登录已失效,请重新登录。" });
} else {
let userId = refDecoded.userId;
const newToken = jwt.sign({ userId }, secretKey, {
expiresIn: "1h",
});
res.setHeader("refreshToken", newToken);
next();
}
});
} else {
next();
}
});
} else {
next();
}
});
router.use("/login", login);
router.use("/user", user);
router.use("/system-menue", systemMenue);
router.use("/role", role);
router.use("/public/", publicList);
router.use("/deceased", deceased);
router.use("/deceased-retail", deceasedRetail);
router.use("/service-item", serviceItem);
router.use("/service-category", serviceCategory);
router.use("/checkout", checkout);
router.use("/retail-checkout", retailCheckout);
router.use("/no-deceased-retail", noDeceasedRetail);
router.use("/cancel", cancleExamine);
router.use("/checkout-retail", CheckoutRetail);
router.use("/stats", Stats);
export default router;

View File

@@ -0,0 +1,117 @@
import * as express from "express";
import { Request, Response } from "express";
import { getConnection } from "typeorm";
import User from "../../entity/User";
import { Role } from "../../entity/Role";
import { SystemMenue } from "../../entity/SystemMenue";
import menueToTree from "../../lib/menueToTree";
const router = express.Router();
let jwt = require("jsonwebtoken");
const secretKey = "myNameIsLiJiaTu";
// 递归获取所有父级菜单的ID
async function getAllParentIds(menuIds: number[]): Promise<number[]> {
const parentIds = new Set<number>();
const getParents = async (id: number) => {
let menueRep = getConnection().getRepository(SystemMenue);
const menu = await menueRep.findOne({ where: { id } });
if (menu && menu.parentId !== 0) {
parentIds.add(menu.parentId);
await getParents(menu.parentId);
}
};
await Promise.all(menuIds.map((id) => getParents(id)));
return Array.from(parentIds);
}
router.post("/", async (req: Request, res: Response) => {
const { name, pwd } = req.body;
const connection = getConnection();
const user = connection.getRepository(User);
if (!(await user.findAndCount())[0].length && name === "admin") {
let userInstance = new User();
userInstance = {
...userInstance,
name: "admin",
pwd: "E10ADC3949BA59ABBE56E057F20F883E",
role: "1",
userState: 1,
};
userInstance.createDate = userInstance.createDate || new Date();
userInstance.updateDate = userInstance.updateDate || new Date();
await connection.getRepository(User).save(userInstance);
}
const findUser = await user.findOne({ name });
if (!findUser) return res.send("该用户不存在,请检查!");
if (findUser && findUser.pwd !== pwd) return res.send("密码错误,请检查!");
if (findUser.userState === 0 && findUser.name !== "admin")
return res.send({ code: 500, msg: "该账号被禁止使用,请联系管理员!" });
let userRole = findUser.role;
let roleRep = connection.getRepository(Role);
if (findUser.name === "admin") {
let role = await roleRep.findOne({ where: { id: Number(userRole) } });
role.values = (
await connection.getRepository(SystemMenue).findAndCount()
)[0]
.map((item) => item.id.toString())
.join(",");
if (role.values.length !== findUser.role.length) {
await roleRep.save(role);
}
}
let roleRes = await roleRep
.createQueryBuilder("role")
.where("role.id IN (:...userRole)", { userRole: userRole.split(",") })
.getOne();
if (roleRes) {
let menueRep = connection.getRepository(SystemMenue);
// 在原有查询中增加父级查询
const selectedMenuIds = roleRes.values.split(",").map(Number); // [34,33,24]
const parentIds = await getAllParentIds(selectedMenuIds); // 获取所有父级ID如26
const finalMenuIds = Array.from(
new Set([...selectedMenuIds, ...parentIds])
); // 合并选中的ID和父级ID
let menueList = await menueRep
.createQueryBuilder("system_menue")
.where("system_menue.id IN (:...menue)", { menue: finalMenuIds })
.getMany();
findUser["routerMenue"] = menueToTree(menueList);
} else {
findUser["routerMenue"] = [];
}
// 登录成功,生成 JWT
const token = jwt.sign({ userId: findUser.id }, secretKey, {
expiresIn: "1h",
});
const refreshToken = jwt.sign({ userId: findUser.id }, secretKey, {
expiresIn: "1d",
});
// 过滤密码
delete findUser.pwd;
return res.send({
code: 200,
msg: "登录成功!",
data: {
token: "Bearer " + token,
refreshToken,
user: findUser,
},
});
});
export default router;

View File

@@ -0,0 +1,212 @@
import * as express from "express";
import { getConnection, getRepository } from "typeorm";
import DeceasedRetail from "../../entity/DeceasedRetail";
import curd from "@/lib/curd/curd";
import SeletedServiceList from "@/entity/SeletedServiceList";
import Deceased from "@/entity/Deceased";
const router = express.Router();
async function GetData(result) {
const ids = Array.from(
new Set(result.list.map((item) => Number(item.deceasedId)))
);
if (ids.length) {
const manyResult = await getConnection()
.getRepository(Deceased)
.createQueryBuilder("deceased")
.where("deceased.id IN (:...ids)", {
ids,
})
.getMany();
result.list = result.list.map((item: any) => {
let findData = manyResult.find(
(fitem: any) => fitem.id === item.deceasedId
);
return {
...findData,
...item,
deceased: findData,
};
});
}
}
router.post("/query", async (req, res) => {
try {
const result = await curd({
entity: DeceasedRetail,
req,
params: {
retailType: 2,
},
}).queryList();
await GetData(result);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.get("/list", async (req, res) => {
try {
const result: any = await curd({
entity: DeceasedRetail,
req,
params: {
retailType: 2,
},
}).queryList();
await GetData(result);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.get("/checkout", async (req, res) => {
let { id } = req.query;
if (!id) {
return res.status(500).send({ code: 500, msg: "未传入结账id" });
}
let deceasedRetailRep = getConnection().getRepository(DeceasedRetail);
let deceasedRetail = await deceasedRetailRep.findOne(Number(id));
if (!deceasedRetail)
return res.status(500).send({ code: 500, msg: "该记录不存在" });
deceasedRetail.retailState = 1;
await deceasedRetailRep.save(deceasedRetail);
res.status(200).send({ code: 200, data: deceasedRetail, msg: "结账成功!" });
});
router.post("/add", async (req, res) => {
delete req.body.id;
const connection = getConnection();
try {
const tempServiceItems = req.body.services.map((item) => {
delete item.id;
return item;
});
req.body.serviceItems = "";
const deceasedRetailRep = connection.getRepository(DeceasedRetail);
let newDeceasedRetail = new DeceasedRetail();
newDeceasedRetail = Object.assign(newDeceasedRetail, req.body);
newDeceasedRetail.retailType = 2;
if (tempServiceItems.length) {
tempServiceItems.forEach((item) => {
item.retailId = newDeceasedRetail.id;
item.hasDeceased = 0;
});
let executeData = await connection
.getRepository(SeletedServiceList)
.save(tempServiceItems);
newDeceasedRetail.serviceItems = executeData
.map((item) => item.id)
.join(",");
await deceasedRetailRep.save(newDeceasedRetail);
}
res.send({ code: 200, msg: "添加成功", data: newDeceasedRetail });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.post("/update", async (req, res) => {
const id = Number(req.body.id);
if (!id) {
return res.send({ code: 400, msg: "逝者ID不能为空" });
}
try {
const connection = getConnection();
const deceasedRepository = connection.getRepository(DeceasedRetail);
const tempServiceItems = req.body.services.map((item) => {
delete item.id;
return item;
});
let findData = await deceasedRepository.findOne(id);
const ids = findData.serviceItems.split(",").map((id) => Number(id));
if (ids.length) {
await connection
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.delete()
.execute();
}
findData = { ...findData, ...req.body };
if (tempServiceItems.length) {
tempServiceItems.forEach((item) => {
item.retailId = id;
item.hasDeceased = 0;
item.updateDate = new Date();
item.createDate = new Date();
});
let executeData = await connection
.getRepository(SeletedServiceList)
.save(tempServiceItems);
findData.serviceItems = executeData.map((item) => item.id).join(",");
await deceasedRepository.save(findData);
}
res.send({ code: 200, msg: "更新成功", data: findData });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.get("/delete", async (req, res) => {
const id = Number(req.query.id);
if (!id) {
return res.send({ code: 400, msg: "数据ID不能为空" });
}
try {
const connection = getConnection();
const deceasedRepository = connection.getRepository(DeceasedRetail);
const findData = await deceasedRepository.findOne(id);
const ids = findData.serviceItems.split(",").map((id) => Number(id));
if (ids.length) {
await connection
.getRepository(SeletedServiceList)
.createQueryBuilder("seleted_service_list")
.where("seleted_service_list.id IN (:...ids)", {
ids,
})
.delete()
.execute();
}
await deceasedRepository.remove(findData);
res.send({ code: 200, msg: "删除成功!" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,65 @@
import * as express from "express";
import { getConnection } from "typeorm";
import User from "../../entity/User";
import { Role } from "@/entity/Role";
import ServiceCategory from "@/entity/ServiceCategory"; // 请确保路径正确
const router = express.Router();
router.get("/guide", async (req, res) => {
let roleRep = getConnection().getRepository(Role);
let guide1ID = (await roleRep.findOne({ name: "服务部" })).id;
let guideIds = [guide1ID]
.filter((item) => item)
.map((item) => item.toString());
let userList = await getConnection()
.getRepository(User)
.createQueryBuilder("user")
.where("user.role IN (:...ids)", {
ids: guideIds,
})
.getMany();
return res.send({
code: 200,
data: userList.map((item) => {
return {
value: item.name,
label: item.name,
};
}),
});
});
router.get("/service-categories", async (req, res) => {
try {
// 获取服务分类Repository
const categoryRepo = getConnection().getRepository(ServiceCategory);
// 查询所有分类(可按需要添加排序)
const categories = await categoryRepo.find({
order: { createDate: "DESC" }, // 按创建时间倒序
});
// 格式化返回数据
return res.status(200).json({
code: 200,
data: categories.map((item) => ({
value: item.id, // 使用ID作为值
label: item.name, // 显示分类名称
// 可根据需要添加额外字段
name: item.name,
// parentId: item.parentId
})),
});
} catch (error) {
return res.status(500).json({
code: 500,
msg: "获取服务分类失败,请稍后重试",
});
}
});
export default router;

View File

@@ -0,0 +1,130 @@
import * as express from "express";
import { getConnection } from "typeorm";
import ServiceItem from "../../entity/ServiceItem";
import curd from "@/lib/curd/curd";
import DeceasedRetail from "@/entity/DeceasedRetail";
import Deceased from "@/entity/Deceased";
import noDeceasedRetail from "@/entity/noDeceasedRetail";
import PaymentRecord from "@/entity/Payment";
const router = express.Router();
async function GetData(result) {
let paymentIds = result.list
.filter((item) => item.retailState === 1)
.map((item) => item.id);
if (paymentIds.length) {
let manyResult = await getConnection()
.getRepository(PaymentRecord)
.createQueryBuilder("PaymentRecord")
.where("PaymentRecord.deceased_retail_id IN (:...ids)", {
ids: paymentIds,
})
.getMany();
result.list = result.list.map((item: any) => {
return {
...item,
payment: manyResult.find((fitem) => fitem.deceasedRetailId === item.id),
};
});
}
}
router.get("/list", async (req, res) => {
let retailType = Number(req.query.retailType || 0);
try {
let result = await curd({
entity: DeceasedRetail,
params: {
cancelState: 0,
retailType,
...req.body,
},
req,
}).queryList();
if (retailType === 1) {
const ids = Array.from(
new Set(result.list.map((item) => Number(item.deceasedId)))
);
if (ids.length) {
const manyResult = await getConnection()
.getRepository(Deceased)
.createQueryBuilder("deceased")
.where("deceased.id IN (:...ids)", {
ids,
})
.getMany();
result.list = result.list.map((item: any) => {
let findData = manyResult.find(
(fitem: any) => fitem.id === item.deceasedId
);
return {
...findData,
...item,
deceased: findData,
};
});
}
}
await GetData(result);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.post("/query", async (req, res) => {
let retailType = Number(req.body.retailType);
try {
let result = await curd({
entity: DeceasedRetail,
params: {
cancelState: 0,
retailType,
...req.body,
},
req,
}).queryList();
if (retailType === 1) {
const ids = Array.from(
new Set(result.list.map((item) => Number(item.deceasedId)))
);
if (ids.length) {
const manyResult = await getConnection()
.getRepository(Deceased)
.createQueryBuilder("deceased")
.where("deceased.id IN (:...ids)", {
ids,
})
.getMany();
result.list = result.list.map((item: any) => {
let findData = manyResult.find(
(fitem: any) => fitem.id === item.deceasedId
);
return {
...findData,
...item,
};
});
}
}
await GetData(result);
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,70 @@
import * as express from "express";
import { getConnection } from "typeorm";
import pagination from "../../lib/pagination/pagination";
import dayjs from "dayjs";
import { Role } from "../../entity/Role";
import curd from "@/lib/curd/curd";
let router = express.Router();
router.get("/list", async (req, res) => {
let data = await pagination(Role, req);
return res.send({
code: 200,
data,
});
});
router.post("/add", async (req, res) => {
let body = req.body;
let roleRep = getConnection().manager.getRepository(Role);
let findRole = await roleRep.findOne({ where: { name: body.name } });
if (findRole) return res.send({ code: 500, msg: "角色名已存在!" });
await curd({ entity: Role, req }).add();
return res.send({ code: 200, msg: "角色添加成功!" });
});
router.post("/query", async (req, res) => {
let data = await curd({ entity: Role, req }).queryList();
return res.send({
code: 200,
data,
});
});
router.get("/delete", async (req, res) => {
let roleRep = getConnection().manager.getRepository(Role);
let { id } = req.query;
let findMnue = await roleRep.find({ where: { id } });
if (!findMnue)
return res.send({ code: 500, msg: "删除的角色不存在,请检查!" });
await roleRep.remove(findMnue);
return res.send({ code: 200, msg: "删除成功!" });
});
router.post("/update", async (req, res, next) => {
if (!req.body.id)
return res.send({ code: 502, msg: "未传入角色ID请检查数据" });
const bodyData = req.body;
const roleRep = getConnection().manager.getRepository(Role);
let findRole = await roleRep.findOne({ where: { id: bodyData.id } });
findRole = { ...findRole, ...bodyData };
findRole.updateDate = new Date();
await roleRep.save(findRole);
return res.send({
code: 200,
msg: "角色修改成功!",
data: findRole,
});
});
export default router;

View File

@@ -0,0 +1,155 @@
import * as express from "express";
import { getConnection } from "typeorm";
import ServiceCategory from "../../entity/ServiceCategory";
import curd from "@/lib/curd/curd";
const router = express.Router();
router.get("/list", async (req, res) => {
req.body.pageSize = "1000";
req.query.pageSize = "1000";
try {
if (req.query.all) {
req.body.pageSize = "1000";
req.query.pageSize = "1000";
const result = await curd({ entity: ServiceCategory, req }).queryList();
return res.send({ code: 200, data: result });
}
// 查询数据库中的所有分类数据
const result = await curd({ entity: ServiceCategory, req }).queryList();
// 将数据转换为树形结构
const buildTree = (items, parentId = 0) => {
return items
.filter((item) => item.parentId === parentId)
.map((item) => ({
...item,
children: buildTree(items, item.id), // 递归构建子节点
}));
};
const treeData = buildTree(result.list);
result.list = treeData;
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
router.post("/query", async (req, res) => {
try {
// 查询数据库中的所有分类数据
const result = await curd({ entity: ServiceCategory, req }).queryList();
// 将数据转换为树形结构
// const buildTree = (items, parentId = 0) => {
// return items
// .filter((item) => item.parentId === parentId)
// .map((item) => ({
// ...item,
// children: buildTree(items, item.id), // 递归构建子节点
// }));
// };
// const treeData = buildTree(result.list);
// result.list = treeData;
res.send({ code: 200, data: result });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 添加商品分类
router.post("/add", async (req, res) => {
const { name } = req.body;
if (!name) {
return res.send({ code: 400, msg: "商品分类名称不能为空!" });
}
try {
const connection = getConnection();
const categoryRepository = connection.getRepository(ServiceCategory);
// 校验是否已存在相同名称的商品分类
const existingCategory = await categoryRepository.findOne({
where: { name },
});
if (existingCategory) {
return res.send({ code: 400, msg: "商品分类名称已存在,请勿重复添加!" });
}
let newCategory = new ServiceCategory();
newCategory = {
...newCategory,
...req.body,
};
await categoryRepository.save(newCategory);
res.send({ code: 200, msg: "商品分类添加成功", data: newCategory });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 更新商品分类
router.post("/update", async (req, res) => {
const { id } = req.body;
if (!id) {
return res.send({ code: 400, msg: "商品分类ID不能为空" });
}
try {
const connection = getConnection();
const categoryRepository = connection.getRepository(ServiceCategory);
// 查找商品分类
let category = await categoryRepository.findOne(Number(id));
if (!category) {
return res.send({ code: 400, msg: "商品分类不存在!" });
}
// 更新字段
category = {
...category,
...req.body,
};
await categoryRepository.save(category);
res.send({ code: 200, msg: "商品分类更新成功", data: category });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 删除商品分类
router.get("/delete", async (req, res) => {
const { id } = req.query;
if (!id) {
return res.send({ code: 400, msg: "商品分类ID不能为空" });
}
try {
const connection = getConnection();
const categoryRepository = connection.getRepository(ServiceCategory);
const category = await categoryRepository.findOne(Number(id));
if (!category) {
return res.send({ code: 400, msg: "商品分类不存在!" });
}
await categoryRepository.remove(category);
res.send({ code: 200, msg: "商品分类删除成功" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,112 @@
import * as express from "express";
import { getConnection } from "typeorm";
import ServiceItem from "../../entity/ServiceItem";
import curd from "@/lib/curd/curd";
const router = express.Router();
// 查询服务项目列表(支持分页)
// router.get("/list", async (req, res) => {
// try {
// const result = await curd({ entity: ServiceItem, req }).queryList();
// res.send({ code: 200, data: result });
// } catch (err) {
// res.status(500).send({ code: 500, msg: err.message });
// }
// });
router.get("/list", async (req, res) => {
if (req.query.all) {
req.body.pageSize = "1000";
req.query.pageSize = "1000";
}
let data = await curd({ entity: ServiceItem, req }).queryList();
return res.send({
code: 200,
data,
});
});
router.post("/query", async (req, res) => {
let data = await curd({ entity: ServiceItem, req }).queryList();
return res.send({
code: 200,
data,
});
});
// 添加服务项目
router.post("/add", async (req, res) => {
const { name } = req.body;
if (!name) {
return res.send({ code: 400, msg: "服务项目名称不能为空!" });
}
const connection = getConnection();
const itemRepository = connection.getRepository(ServiceItem);
if (await itemRepository.findOne({ name })) {
return res.send({ code: 400, msg: "服务项目名称已存在!" });
}
try {
const newDeceased = await curd({ entity: ServiceItem, req }).add();
res.send({ code: 200, msg: "服务项目添加成功", data: newDeceased });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 更新服务项目
router.post("/update", async (req, res) => {
const { id, name } = req.body;
if (!id) {
return res.send({ code: 400, msg: "服务项目ID不能为空" });
}
const connection = getConnection();
const itemRepository = connection.getRepository(ServiceItem);
try {
// 查找服务项目
let serviceItem = await itemRepository.findOne({ id });
if (!serviceItem) {
return res.send({ code: 400, msg: "服务项目不存在!" });
}
if (serviceItem.id !== id && serviceItem.name === name) {
return res.send({ code: 400, msg: "服务项目名称已存在!" });
}
serviceItem = {
...serviceItem,
...req.body,
parentId: req.body.parentId ?? 0,
};
await itemRepository.save(serviceItem);
res.send({ code: 200, msg: "服务项目更新成功", data: serviceItem });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
// 删除服务项目
router.get("/delete", async (req, res) => {
const { id } = req.query;
if (!id) {
return res.send({ code: 400, msg: "服务项目ID不能为空" });
}
try {
const connection = getConnection();
const itemRepository = connection.getRepository(ServiceItem);
const item = await itemRepository.findOne(Number(id));
if (!item) {
return res.send({ code: 400, msg: "服务项目不存在!" });
}
await itemRepository.remove(item);
res.send({ code: 200, msg: "服务项目删除成功" });
} catch (err) {
res.status(500).send({ code: 500, msg: err.message });
}
});
export default router;

View File

@@ -0,0 +1,121 @@
// controllers/paymentStats.ts
import { Request, Response } from "express";
import { getRepository } from "typeorm";
import dayjs from "dayjs";
import CheckoutPaymentRecords from "@/entity/CheckoutPayment";
import PaymentRecord from "@/entity/Payment";
import DeceasedRetail from "@/entity/DeceasedRetail";
import parseRangDate, { handleError } from "@/util/globalMethods";
// 类型定义
type PaymentStats = {
cashAmount: number;
unionPayAmount: number;
cardAmount: number;
publicTransferAmount: number;
workshopPayment: number;
};
type StatsResponse = {
retail: PaymentStats; // 零售单统计来自PaymentRecord
service: PaymentStats; // 服务单统计来自CheckoutPaymentRecords
total: PaymentStats; // 合并统计
};
export const getPaymentStats = async (req: Request, res: Response) => {
try {
// 参数处理
const { startDate, endDate } = parseRangDate(req);
// 并行获取统计结果
const [retailStats, serviceStats] = await Promise.all([
getRetailStats(startDate, endDate),
getServiceStats(startDate, endDate),
]);
// 构建响应
const response: StatsResponse = {
retail: retailStats,
service: serviceStats,
total: {
cashAmount: retailStats.cashAmount + serviceStats.cashAmount,
unionPayAmount:
retailStats.unionPayAmount + serviceStats.unionPayAmount,
cardAmount: retailStats.cardAmount + serviceStats.cardAmount,
publicTransferAmount:
retailStats.publicTransferAmount + serviceStats.publicTransferAmount,
workshopPayment:
retailStats.workshopPayment + serviceStats.workshopPayment,
},
};
res.json({ code: 200, data: response });
} catch (error) {
handleError(res, error);
}
};
// 修改后的获取零售单统计
const getRetailStats = async (
start: string,
end: string
): Promise<PaymentStats> => {
const result = await getRepository(PaymentRecord)
.createQueryBuilder("payment")
.select([
"SUM(payment.cash_amount) AS cashAmount",
"SUM(payment.union_pay_amount) AS unionPayAmount",
"SUM(payment.card_amount) AS cardAmount",
"SUM(payment.public_transfer_amount) AS publicTransferAmount",
"SUM(payment.workshop_payment) AS workshopPayment",
])
.where("payment.checkout_date BETWEEN :start AND :end", { start, end })
.andWhere(
`(payment.deceased_retail_id IN (
SELECT id
FROM deceased_retail
WHERE retail_state = 1
) OR payment.no_deceased_retail_id IN (
SELECT id
FROM deceased_retail
WHERE retail_state = 1
))`
)
.getRawOne();
return formatStats(result);
};
// 修改后的获取服务单统计
const getServiceStats = async (
start: string,
end: string
): Promise<PaymentStats> => {
const result = await getRepository(CheckoutPaymentRecords)
.createQueryBuilder("cpr")
.innerJoin(
DeceasedRetail,
"dr",
"dr.id = cpr.checkout_retail_id AND dr.retail_state = 1"
)
.select([
"SUM(cpr.cash_amount) AS cashAmount",
"SUM(cpr.union_pay_amount) AS unionPayAmount",
"SUM(cpr.card_amount) AS cardAmount",
"SUM(cpr.public_transfer_amount) AS publicTransferAmount",
"SUM(cpr.workshop_payment) AS workshopPayment",
])
.where("cpr.checkout_date BETWEEN :start AND :end", { start, end })
.getRawOne();
return formatStats(result);
};
/** 格式化统计结果 */
const formatStats = (raw: any): PaymentStats => ({
cashAmount: Number(raw?.cashAmount || 0),
unionPayAmount: Number(raw?.unionPayAmount || 0),
cardAmount: Number(raw?.cardAmount || 0),
publicTransferAmount: Number(raw?.publicTransferAmount || 0),
workshopPayment: Number(raw?.workshopPayment || 0),
});

View File

@@ -0,0 +1,199 @@
import { Request, Response } from "express";
import { getRepository } from "typeorm";
import dayjs from "dayjs";
import DeceasedRetail from "@/entity/DeceasedRetail";
import SeletedServiceList from "@/entity/SeletedServiceList";
import Deceased from "@/entity/Deceased";
import { handleError } from "@/util/globalMethods";
interface QueryParams {
startDate?: string;
endDate?: string;
serviceName?: string;
deceasedName?: string;
pageSize?: number;
pageNumber?: number;
guide?: string;
familyName: string;
}
interface SalesDetailItem {
id: string;
checkoutDate: string;
deceasedName: string;
gender: string;
age: number;
idCard: string;
contactPerson: string;
contactPhone: string;
serviceName: string;
price: number;
quantity: number;
amount: number;
remark: string;
}
export const getSalesDetails = async (req: Request, res: Response) => {
try {
const params = parseQueryParams(
req.method === "GET" ? req.query : req.body
);
const pageSize = Number(params.pageSize) || 10;
const pageNumber = Number(params.pageNumber) || 1;
// 第一步:查询 SeletedServiceList 和 DeceasedRetail
const initialQuery = getRepository(SeletedServiceList)
.createQueryBuilder("ssl")
.innerJoinAndMapOne(
"ssl.deceasedRetail",
DeceasedRetail,
"dr",
"FIND_IN_SET(ssl.id, dr.serviceItems) AND dr.retailState = 1 AND dr.cancelState = 0 "
);
if (pageSize !== 999999999) {
// 如果还没有连接 Deceased 表,需要确保连接
if (
(params.deceasedName || params.familyName) &&
!initialQuery.expressionMap.joinAttributes.some(
(join) => join.alias.name === "d"
)
) {
initialQuery.innerJoin(Deceased, "d", "d.id = dr.deceasedId");
}
if (params.startDate || params.endDate) {
initialQuery.andWhere("dr.checkoutDate BETWEEN :start AND :end", {
start:
params.startDate ||
dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss"),
end: params.endDate || dayjs().format("YYYY-MM-DD HH:mm:ss"),
});
}
// 服务名称条件
if (params.serviceName) {
initialQuery.andWhere("ssl.name LIKE :serviceName", {
serviceName: `%${params.serviceName}%`,
});
}
if (params.guide) {
initialQuery.andWhere("dr.guide LIKE :guide", {
guide: `%${params.guide}%`,
});
}
// 逝者姓名条件
if (params.deceasedName) {
initialQuery.andWhere(
"( d.name LIKE :deceasedName OR (dr.deceasedId = 0 AND dr.deceasedName LIKE :deceasedName))",
{
deceasedName: `%${params.deceasedName}%`,
}
);
}
// 逝者家属姓名条件
if (params.familyName) {
initialQuery.andWhere(
"( d.familyName LIKE :familyName OR (dr.deceasedId = 0 AND dr.familyName LIKE :familyName) )",
{
familyName: `%${params.familyName}%`,
}
);
}
}
// 执行初步查询
const initialResult = await initialQuery
.orderBy("dr.checkoutDate", "DESC")
.skip((pageNumber - 1) * pageSize)
.take(pageSize)
.getMany();
// 第二步:根据初步查询结果查询绑定 Deceased
const deceasedIds = initialResult
//@ts-ignore
.map((item) => item.deceasedRetail?.deceasedId)
.filter((id) => id !== undefined && id !== 0);
const deceasedMap = new Map();
if (deceasedIds.length > 0) {
const deceasedList = await getRepository(Deceased)
.createQueryBuilder("d")
.where("d.id IN (:...ids)", { ids: deceasedIds })
.getMany();
deceasedList.forEach((deceased) => {
deceasedMap.set(deceased.id, deceased);
});
}
// 合并结果
const result = initialResult.map((item) => {
//@ts-ignore
if (item.deceasedRetail?.deceasedId !== 0) {
//@ts-ignore
item.deceased = deceasedMap.get(item.deceasedRetail.deceasedId) || {};
} else {
//@ts-ignore
item.deceased = { ...item.deceasedRetail };
}
return item;
});
// 获取总记录数
const total = await initialQuery.getCount();
res.json({
code: 200,
data: {
list: result.map((item) => {
return {
...item,
// @ts-ignore
sum: Number(item.price).toFixed(2) * Number(item.quantity),
};
}),
pageSize,
pageNumber,
total,
},
});
} catch (error) {
handleError(res, error);
}
};
const parseQueryParams = (query: any): QueryParams => {
const {
startDate,
endDate,
serviceName,
deceasedName,
guide,
pageSize,
pageNumber,
familyName,
} = query;
const validateDate = (date: string, field: string) => {
if (date && !dayjs(date).isValid()) {
throw new Error(`${field}格式错误请使用YYYY-MM-DD格式`);
}
};
validateDate(startDate, "开始日期");
validateDate(endDate, "结束日期");
return {
startDate: startDate?.trim(),
endDate: endDate?.trim(),
serviceName: serviceName?.trim(),
deceasedName: deceasedName?.trim(),
guide: guide?.trim(),
pageSize: pageSize ? parseInt(pageSize) : undefined,
pageNumber: pageNumber ? parseInt(pageNumber) : undefined,
familyName: familyName,
};
};

View File

@@ -0,0 +1,272 @@
// controllers/serviceStats.ts
import { Request, Response } from "express";
import { getRepository, In } from "typeorm";
import dayjs from "dayjs";
import DeceasedRetail from "@/entity/DeceasedRetail";
import SeletedServiceList from "@/entity/SeletedServiceList";
import ServiceItem from "@/entity/ServiceItem";
import ServiceCategory from "@/entity/ServiceCategory";
import { handleError } from "@/util/globalMethods";
// 类型定义
interface ServiceStatsItem {
serviceName: string;
price: number;
unit: string;
quantity: number;
subtotal: number;
}
interface CategoryStats {
categoryName: string;
services: ServiceStatsItem[];
totalQuantity: number;
totalAmount: number;
}
interface StatsResponse {
categories: CategoryStats[];
otherCategory: CategoryStats;
grandTotalQuantity: number;
grandTotalAmount: number;
}
interface QueryParams {
startDate?: string;
endDate?: string;
serviceName?: string;
categoryName?: string;
}
export const getServiceStats = async (req: Request, res: Response) => {
try {
// 1. 参数解析和验证
const params = parseQueryParams(
req.method === "GET" ? req.query : req.body
);
// 2. 查询符合条件的零售单(按结账时间)
const retailRecords = await getRepository(DeceasedRetail)
.createQueryBuilder("dr")
.where("dr.checkoutDate BETWEEN :start AND :end", {
start:
params.startDate ||
dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss"),
end: params.endDate || dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss"),
})
.andWhere("dr.retailState = 1 AND dr.cancelState = 0") // 只统计已结账的
.getMany();
// 3. 收集所有服务项目ID从serviceItems字段
const allServiceItemIds = retailRecords
.map((record) => {
try {
return record.serviceItems.split(",").map(Number);
} catch (e) {
return [];
}
})
.flat()
.filter((id) => id > 0); // 过滤无效ID
if (allServiceItemIds.length === 0) {
return res.json(emptyResponse());
}
// 4. 查询这些服务项目在SeletedServiceList中的记录
const selectedServices = await getRepository(SeletedServiceList)
.createQueryBuilder("ssl")
.where("ssl.id IN (:...ids)", { ids: allServiceItemIds })
.getMany();
// 5. 收集所有不重复的服务名称
const serviceNames = Array.from(
new Set(selectedServices.map((s) => s.name))
);
if (serviceNames.length === 0) {
return res.json(emptyResponse());
}
// 6. 分步查询分类信息
// 第一步查询ServiceItem获取parentIds
const serviceItems = await getRepository(ServiceItem)
.createQueryBuilder("si")
.select(["si.name", "si.parentId"])
.where("si.name IN (:...names)", { names: serviceNames })
.getMany();
// 第二步收集所有parentIds
const parentIds = Array.from(
new Set(serviceItems.map((item) => item.parentId))
);
// 第三步:批量查询分类名称
const categories = await getRepository(ServiceCategory)
.createQueryBuilder("sc")
.select(["sc.id", "sc.name"])
.where({ id: In(parentIds) })
.getMany();
// 第四步构建分类ID到名称的映射
const categoryIdToName = new Map<number, string>();
categories.forEach((cat) => categoryIdToName.set(cat.id, cat.name));
// 第五步:构建服务名称到分类的映射
const nameToCategoryMap = new Map<string, string>();
serviceItems.forEach((item) => {
const categoryName = categoryIdToName.get(item.parentId) || "其他服务";
nameToCategoryMap.set(item.name, categoryName);
});
// 7. 初始化统计结构
const categoryStats = new Map<string, ServiceStatsItem[]>();
const otherServices: ServiceStatsItem[] = [];
// 8. 处理每个服务项的统计
const serviceNameToStats = new Map<string, ServiceStatsItem>();
selectedServices.forEach((service) => {
// 应用服务名称过滤条件
if (params.serviceName && !service.name.includes(params.serviceName)) {
return;
}
const existingItem = serviceNameToStats.get(service.name);
if (existingItem) {
// 如果已存在同名服务,则合并数量和金额
existingItem.quantity += service.quantity;
existingItem.subtotal += Number(service.price) * service.quantity;
} else {
// 否则创建新条目
serviceNameToStats.set(service.name, {
serviceName: service.name,
price: Number(service.price),
unit: service.unit,
quantity: service.quantity,
subtotal: Number(service.price) * service.quantity,
});
}
});
// // 9. 将合并后的服务项按分类分组
// const categoryStats = new Map<string, ServiceStatsItem[]>();
// const otherServices: ServiceStatsItem[] = [];
serviceNameToStats.forEach((statsItem, serviceName) => {
// 获取分类名称,默认为"其他服务"
const categoryName = nameToCategoryMap.get(serviceName) || "其他服务";
// 分类到对应分组
if (categoryName === "其他服务") {
otherServices.push(statsItem);
} else {
if (!categoryStats.has(categoryName)) {
categoryStats.set(categoryName, []);
}
categoryStats.get(categoryName)?.push(statsItem);
}
});
// 9. 应用分类过滤条件
if (params.categoryName) {
if (params.categoryName !== "其他服务") {
categoryStats.forEach((_, key) => {
if (key !== params.categoryName) {
categoryStats.delete(key);
}
});
} else {
categoryStats.clear(); // 只保留其他服务
}
}
// 10. 构建最终响应
const response = buildFinalResponse(categoryStats, otherServices);
res.json({ code: 200, data: response });
} catch (error) {
handleError(res, error);
}
};
// 辅助函数
const emptyResponse = (): StatsResponse => ({
categories: [],
otherCategory: {
categoryName: "其他服务",
services: [],
totalQuantity: 0,
totalAmount: 0,
},
grandTotalQuantity: 0,
grandTotalAmount: 0,
});
const buildFinalResponse = (
categoryMap: Map<string, ServiceStatsItem[]>,
otherItems: ServiceStatsItem[]
): StatsResponse => {
const categories: CategoryStats[] = [];
let grandTotalQuantity = 0;
let grandTotalAmount = 0;
// 处理分类数据
categoryMap.forEach((items, categoryName) => {
const totalQuantity = items.reduce((sum, item) => sum + item.quantity, 0);
const totalAmount = items.reduce((sum, item) => sum + item.subtotal, 0);
categories.push({
categoryName,
services: items,
totalQuantity,
totalAmount,
});
grandTotalQuantity += totalQuantity;
grandTotalAmount += totalAmount;
});
// 处理其他分类
const otherTotalQuantity = otherItems.reduce(
(sum, item) => sum + item.quantity,
0
);
const otherTotalAmount = otherItems.reduce(
(sum, item) => sum + item.subtotal,
0
);
grandTotalQuantity += otherTotalQuantity;
grandTotalAmount += otherTotalAmount;
return {
categories,
otherCategory: {
categoryName: "其他服务",
services: otherItems,
totalQuantity: otherTotalQuantity,
totalAmount: otherTotalAmount,
},
grandTotalQuantity,
grandTotalAmount,
};
};
const parseQueryParams = (query: any): QueryParams => {
const { startDate, endDate, serviceName, categoryName } = query;
// 日期格式验证
const validateDate = (date: string, field: string) => {
if (date && !dayjs(date).isValid()) {
throw new Error(`${field}格式错误请使用YYYY-MM-DD格式`);
}
};
validateDate(startDate, "开始日期");
validateDate(endDate, "结束日期");
return {
startDate: startDate?.trim(),
endDate: endDate?.trim(),
serviceName: serviceName?.trim(),
categoryName: categoryName?.trim(),
};
};

View File

@@ -0,0 +1,13 @@
// controllers/paymentStats.ts
import { Router } from "express";
import { getPaymentStats } from "./controller/dayIncome";
import { getServiceStats } from "./controller/serviceStats";
import { getSalesDetails } from "./controller/salesDetail";
const router = Router();
router.get("/dayIncome", getPaymentStats);
router.get("/servicesStats", getServiceStats);
router.get("/salesDetails", getSalesDetails);
router.post("/salesDetails", getSalesDetails);
export default router;

View File

@@ -0,0 +1,102 @@
import * as express from "express";
import { getConnection } from "typeorm";
import { SystemMenue } from "../../entity/SystemMenue";
import pagination from "../../lib/pagination/pagination";
import dayjs from "dayjs";
import { filterObjEmptyVal } from "../../util/globalMethods";
const router = express.Router();
router.get("/list", async (req, res) => {
let isAll = req.query.all;
req.query.pageSize = "1000";
req.body.pageSize = "1000";
let data = await pagination(SystemMenue, req);
let dataList = [];
for (let i = 0; i < data.list.length; i++) {
let item = data.list[i];
item.children = [];
for (let j = 0; j < data.list.length; j++) {
let tempItem = data.list[j];
if (tempItem.parentId === item.id) {
item.children.push(tempItem);
}
}
// 只添加父级
if (!item.parentId || isAll) dataList.push(item);
}
data.list = dataList;
data.total = dataList.length;
return res.send({
code: 200,
data,
});
});
router.post("/query", async (req, res) => {
let params = req.body;
let data = await pagination(SystemMenue, req, filterObjEmptyVal(params));
return res.send({
code: 200,
data,
});
});
router.post("/add", async (req, res) => {
let menueRep = getConnection().manager.getRepository(SystemMenue);
let body = req.body;
let findMenue = await menueRep.findOne({ where: { name: body.name } });
if (findMenue) return res.send({ code: 403, msg: `${body.name}已经存在了` });
let menueInstance = new SystemMenue();
menueInstance = body;
menueInstance.createDate = dayjs().format("YYYY-MM-DD HH:mm:ss");
await menueRep.save(menueInstance);
return res.send({
code: 200,
msg: "菜单增加成功!",
});
});
router.post("/update", async (req, res) => {
let menueRep = getConnection().manager.getRepository(SystemMenue);
let body = req.body as SystemMenue;
let filterMenue = await menueRep.findOne({ where: { name: body.name } });
if (
filterMenue &&
filterMenue.id !== body.id &&
body.name === filterMenue.name
) {
return res.send({ code: 500, msg: "菜单名已经存在" });
}
let findMenue = await menueRep.findOne({ where: { id: body.id } });
findMenue = { ...findMenue, ...body };
findMenue.updateDate = dayjs().format("YYYY-MM-DD HH:mm:ss");
await menueRep.save(findMenue);
return res.send({
code: 200,
msg: "菜单修改成功!",
});
});
router.get("/delete", async (req, res) => {
let menueRep = getConnection().manager.getRepository(SystemMenue);
let { id } = req.query;
let findMnue = await menueRep.find({ where: { id } });
if (!findMnue)
return res.send({ code: 500, msg: "删除菜单不存在,请检查!" });
await menueRep.remove(findMnue);
return res.send({ code: 200, msg: "删除成功!" });
});
export default router;

View File

@@ -0,0 +1,116 @@
import * as express from "express";
import { getConnection } from "typeorm";
import User from "../../entity/User";
import pagination from "../../lib/pagination/pagination";
import dayjs from "dayjs";
import curd from "@/lib/curd/curd";
const router = express.Router();
router.post("/query", async (req, res) => {
const findUsers = await curd({ entity: User, req }).query();
if (!findUsers.length) return res.send({ code: 200, data: [] });
// 过滤密码
findUsers.map((item) => {
// @ts-ignore
delete item.pwd;
return item;
});
return res.send({
code: 200,
data: {
list: findUsers,
},
});
});
router.get("/list", async (req, res) => {
let data = await pagination(User, req);
return res.send({
code: 200,
data: data,
});
});
router.post("/add", async (req, res) => {
const connection = getConnection();
const bodyData = req.body;
if (!bodyData.name?.length)
return res.send({ code: 502, msg: "用户名是必须的!" });
const userRep = connection.getRepository(User);
const findUser = await userRep.findOne({ name: bodyData.name });
if (findUser)
return res.send({ code: 403, msg: `${bodyData.name}已经存在了!` });
let userInstance = new User();
userInstance = { ...userInstance, ...bodyData };
userInstance.createDate = userInstance.createDate || new Date();
userInstance.updateDate = userInstance.updateDate || new Date();
userInstance.pwd = "E10ADC3949BA59ABBE56E057F20F883E"; // 默认用户密码123456
userInstance.birthday = dayjs(userInstance.birthday).format(
"YYYY-MM-DD HH:mm:ss"
); // 用户默认生日
await userRep.save(userInstance);
return res.send({
code: 200,
msg: "用户注册成功!",
});
});
router.post("/update", async (req, res, next) => {
if (!req.body.id)
return res.send({ code: 502, msg: "未传入用户ID请检查数据" });
if (!req.body.pwd) delete req.body.pwd;
const findUser = await curd({ entity: User, req }).update();
return res.send({
code: 200,
msg: "用户修改成功!",
data: {
userInfo: findUser,
},
});
});
router.get("/delete", async (req, res) => {
const connection = getConnection();
const userRep = connection.getRepository(User);
const { id } = req.query;
const findUser = await userRep.findOne(id as string);
if (findUser) {
await userRep.remove(findUser);
return res.send({
code: 200,
msg: "用户删除成功!",
});
} else {
return res.send({
code: 401,
msg: "要删除的用户不存在!",
});
}
});
router.get("/resetPwd", async (req, res) => {
let userRep = getConnection().getRepository(User);
let { id } = req.query;
let findUser = await userRep.findOne(id as string);
if (findUser) {
findUser.pwd = "E10ADC3949BA59ABBE56E057F20F883E"; // 默认用户密码123456
await userRep.save(findUser);
return res.send({ code: 200, msg: "用户密码重置成功!" });
} else {
return res.send({ code: 500, msg: "用户不存在!" });
}
});
export default router;

View File

@@ -0,0 +1,81 @@
import dayjs from "dayjs";
import { Request, Response } from "express";
export function filterObjEmptyVal(obj: { [key: string]: any }) {
if (!obj) return {};
if (Array.isArray(obj))
throw Error("你传入了一个数组需要的是一个Object对象");
let emptyObj = {};
for (let [key, value] of Object.entries(obj)) {
if (value || value === 0) emptyObj[key] = value;
}
return emptyObj;
}
export function getNowDateStr() {
return dayjs().format("YYYY-MM-DD HH:mm:ss");
}
export function buildTree(items, parentId = 0) {
return items
.filter((item) => item.parentId === parentId)
.map((item) => ({
...item,
children: buildTree(items, item.id),
}));
}
export function getPaginationParams(req: Request): {
pageSize: number;
pageNumber: number;
} {
const { pageSize = 10, pageNumber = 1 } =
req.method === "GET" ? req.query : req.body;
return {
pageSize: Math.max(1, parseInt(pageSize as string, 10)),
pageNumber: Math.max(1, parseInt(pageNumber as string, 10)),
};
}
/** 统一错误处理 */
export function handleError(res: Response, error: any) {
const message = error instanceof Error ? error.message : "未知错误";
res.status(500).json({
code: 500,
msg: message,
});
}
/** 处理日期参数 */
export default function parseRangDate(req: Request) {
let { startDate: reqStart, endDate: reqEnd } =
req.method === "GET" ? req.query : req.body;
const dateFormat = "YYYY-MM-DD HH:mm:ss";
if (!reqStart && !reqEnd) return {};
// 验证必填参数
if (!reqStart) reqStart = dayjs().startOf("day").toDate();
// 处理起始日期
const start = dayjs(reqStart as string, dateFormat);
if (!start.isValid()) throw new Error("起始日期格式错误");
// 处理结束日期
let end = start;
if (reqEnd) {
end = dayjs(reqEnd as string, dateFormat);
if (!end.isValid()) throw new Error("结束日期格式错误");
if (end.isBefore(start)) throw new Error("结束日期不能早于起始日期");
}
// 生成时间范围
return {
startDate: start.format("YYYY-MM-DD HH:mm:ss"),
endDate: (reqEnd ? end : dayjs()).format("YYYY-MM-DD HH:mm:ss"),
};
}

27
backEnd/tsconfig.json Normal file
View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"lib": ["es5", "es6", "ES2022"],
"paths": {
"router/*": ["./src/router/*"],
"@/*": ["./src/*"]
},
"baseUrl": "./",
"declaration": true, // 是否生成.d.ts文件
"declarationDir": "./types", // .d.ts文件输出路径
"target": "ES5", // 编译目标
"module": "CommonJS",
// "strict": true,
"moduleResolution": "Node",
"outDir": "./build",
"emitDecoratorMetadata": true,
"experimentalDecorators": true, // 用于指定是否启用实验性的装饰器特性
"sourceMap": false,
// "resolveJsonModule": true,
"allowSyntheticDefaultImports": true, // 用于允许从没有默认导出的模块中默认导入
"esModuleInterop": true, //是否允许export=导出import from导入
},
// "ts-node": {
// "esm": true
// },
"include": ["src/**/*", "index.ts"]
}

1
backEnd/types/index.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
import "./src/util/dotenvConfig";

View File

@@ -0,0 +1,5 @@
export declare abstract class BaseEntity {
id: number;
createDate: string;
updateDate: string;
}

6
backEnd/types/src/entity/Role.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
import { BaseEntity } from "../abstrClass/BaseEntity";
export declare class Role extends BaseEntity {
values: string;
roleState: number;
name: string;
}

View File

@@ -0,0 +1,10 @@
export declare class SystemMenu {
id: number;
name: string;
createDate: string;
updateDate: string;
path: string;
parentId: number;
icon: string;
show: boolean;
}

18
backEnd/types/src/entity/User.d.ts vendored Normal file
View File

@@ -0,0 +1,18 @@
export declare class User {
id: number;
name: string;
phone: string;
sex: string;
pwd: string;
createDate: string;
updateDate: string;
userState: number;
role: string;
birthday: string;
age: string;
province: string;
city: string;
area: string;
address: string;
}
export default User;

30
backEnd/types/src/lib/curd/curd.d.ts vendored Normal file
View File

@@ -0,0 +1,30 @@
import { Request } from "express";
interface CurdOptions {
entity: any;
req: Request;
params: {
[key: string]: any;
};
}
declare class Curd {
private entity;
private req;
private queryParams;
private repositrory;
constructor(options: CurdOptions);
add(): Promise<{
code: number;
data: any;
}>;
delete(): Promise<{
code: number;
msg: string;
}>;
update(): Promise<{
code: number;
data: any;
}>;
query(): Promise<unknown[]>;
}
export default function (options: CurdOptions): Curd;
export {};

View File

@@ -0,0 +1,6 @@
import { SystemMenu } from "../../src/entity/SystemMenu";
interface routerTree extends SystemMenu {
children?: SystemMenu[];
}
export default function menueToTree(data: SystemMenu[]): routerTree[];
export {};

View File

@@ -0,0 +1,17 @@
import { Request } from "express";
interface paginationType {
list: any[];
total: number;
pageSize: number;
pageNumber: number;
}
/**
*
* @param entite 传入实体类以供查询
* @param req express的Request请求体
* @returns
*/
export default function getPagination(entite: any, req: Request, queryParams?: {
[key: string]: any;
}): Promise<paginationType>;
export {};

2
backEnd/types/src/router/index.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
declare let router: import("express-serve-static-core").Router;
export default router;

View File

@@ -0,0 +1,2 @@
declare const router: import("express-serve-static-core").Router;
export default router;

View File

@@ -0,0 +1,2 @@
declare let router: import("express-serve-static-core").Router;
export default router;

View File

@@ -0,0 +1,2 @@
declare const router: import("express-serve-static-core").Router;
export default router;

View File

@@ -0,0 +1,2 @@
declare const router: import("express-serve-static-core").Router;
export default router;

View File

@@ -0,0 +1,5 @@
declare const _default: {
development: boolean;
production: boolean;
};
export default _default;

View File

@@ -0,0 +1,2 @@
export declare function filterObjEmptyVal(obj: { [key: string]: any }): {};
export declare function getNowDateStr(): string;

22
backEnd/webpack.config.ts Normal file
View File

@@ -0,0 +1,22 @@
import path from "path";
module.exports = {
entry: "./index.ts",
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".ts", ".js"],
},
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
},
target: "node",
};