This commit is contained in:
2026-04-24 18:17:21 +08:00
parent 524ff8cd32
commit b7ba9a26b0
34 changed files with 9819 additions and 7922 deletions
+6
View File
@@ -16,6 +16,12 @@ request.interceptors.request.use((config) => {
request.interceptors.response.use( request.interceptors.response.use(
(response) => { (response) => {
const { data } = response; const { data } = response;
if (data.code === 401) {
localStorage.removeItem('seller_token');
localStorage.removeItem('seller_info');
window.location.href = '/login';
return Promise.reject(new Error(data.message || '登录已过期'));
}
if (data.code >= 200 && data.code < 300) { if (data.code >= 200 && data.code < 300) {
return data; return data;
} }
+4 -2
View File
@@ -51,7 +51,8 @@ export function request<T = any>(options: RequestOptions): Promise<ApiResponse<T
const responseData = res.data as ApiResponse<T>; const responseData = res.data as ApiResponse<T>;
if (res.statusCode === 401) { // 检查响应体中的 code 是否为 401
if (responseData.code === 401 || res.statusCode === 401) {
// 如果是 skipAuthRedirect,只清除 token 不跳转页面 // 如果是 skipAuthRedirect,只清除 token 不跳转页面
if (skipAuthRedirect) { if (skipAuthRedirect) {
if (useSellerToken) { if (useSellerToken) {
@@ -67,9 +68,10 @@ export function request<T = any>(options: RequestOptions): Promise<ApiResponse<T
// 正常 401 处理,只清除对应账户的 token,不互相干扰 // 正常 401 处理,只清除对应账户的 token,不互相干扰
if (useSellerToken) { if (useSellerToken) {
// 商家 token 失效,只清除商家登录状态,不跳转页面 // 商家 token 失效,跳转到商家登录
uni.removeStorageSync('sellerToken'); uni.removeStorageSync('sellerToken');
uni.removeStorageSync('sellerInfo'); uni.removeStorageSync('sellerInfo');
uni.navigateTo({ url: '/pages/seller/register?mode=login' });
} else { } else {
// 用户 token 失效,清除用户登录状态并跳转登录页 // 用户 token 失效,清除用户登录状态并跳转登录页
uni.removeStorageSync('token'); uni.removeStorageSync('token');
+7 -5
View File
@@ -34,11 +34,13 @@ export const getInviteConfig = () =>
// 更新邀请活动配置 // 更新邀请活动配置
export const updateInviteConfig = (data: { export const updateInviteConfig = (data: {
firstOrderRate?: number; config?: {
secondOrderRate?: number; firstOrderRate?: number;
minCashback?: number; secondOrderRate?: number;
maxCashback?: number; minCashback?: number;
withdrawThreshold?: number; maxCashback?: number;
withdrawThreshold?: number;
};
enabled?: boolean; enabled?: boolean;
}) => }) =>
request.put('/admin/activity/invite/config', data); request.put('/admin/activity/invite/config', data);
@@ -77,8 +77,8 @@ const InviteManage: React.FC = () => {
try { try {
const res = await getInviteConfig(); const res = await getInviteConfig();
setConfig(res.data); setConfig(res.data);
if (res.data) { if (res.data?.config) {
form.setFieldsValue(res.data); form.setFieldsValue(res.data.config);
} }
} finally { } finally {
setConfigLoading(false); setConfigLoading(false);
@@ -121,9 +121,9 @@ const InviteManage: React.FC = () => {
const handleSaveConfig = async () => { const handleSaveConfig = async () => {
try { try {
const values = await form.validateFields(); const values = await form.validateFields();
await updateInviteConfig(values); await updateInviteConfig({ config: values });
message.success('配置已保存'); message.success('配置已保存');
setConfig({ ...config, ...values }); setConfig({ ...config, config: { ...config?.config, ...values } });
} catch {} } catch {}
}; };
+1 -1
View File
@@ -40,7 +40,7 @@ import { ScheduleModule as TaskScheduleModule } from './schedule/schedule.module
password: process.env.DB_PASSWORD || '', password: process.env.DB_PASSWORD || '',
database: process.env.DB_DATABASE || 'rent_platform', database: process.env.DB_DATABASE || 'rent_platform',
entities: [__dirname + '/entities/**/*.entity{.ts,.js}'], entities: [__dirname + '/entities/**/*.entity{.ts,.js}'],
synchronize: process.env.NODE_ENV === 'development', synchronize: false,
logging: process.env.NODE_ENV === 'development', logging: process.env.NODE_ENV === 'development',
charset: 'utf8mb4', charset: 'utf8mb4',
timezone: '+08:00', timezone: '+08:00',
+4 -4
View File
@@ -29,15 +29,15 @@ export class Admin {
@Column({ type: 'enum', enum: ['active', 'frozen'], default: 'active', comment: '状态' }) @Column({ type: 'enum', enum: ['active', 'frozen'], default: 'active', comment: '状态' })
status: 'active' | 'frozen'; status: 'active' | 'frozen';
@Column({ type: 'datetime', nullable: true, comment: '最后登录时间' }) @Column({ name: 'last_login_at', type: 'datetime', nullable: true, comment: '最后登录时间' })
lastLoginAt: Date; lastLoginAt: Date;
@Column({ length: 50, nullable: true, comment: '最后登录IP' }) @Column({ name: 'last_login_ip', length: 50, nullable: true, comment: '最后登录IP' })
lastLoginIp: string; lastLoginIp: string;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
+1 -1
View File
@@ -44,6 +44,6 @@ export class Coupon {
@Column({ name: 'created_by', type: 'bigint', unsigned: true, nullable: true, comment: '创建人ID' }) @Column({ name: 'created_by', type: 'bigint', unsigned: true, nullable: true, comment: '创建人ID' })
createdBy: number; createdBy: number;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
} }
+10 -10
View File
@@ -13,7 +13,7 @@ export class Merchant {
@Column({ name: 'seller_id', type: 'bigint', unsigned: true, unique: true, comment: '关联商家账户ID' }) @Column({ name: 'seller_id', type: 'bigint', unsigned: true, unique: true, comment: '关联商家账户ID' })
sellerId: number; sellerId: number;
@Column({ length: 100, comment: '店铺名称' }) @Column({ name: 'shop_name', length: 100, comment: '店铺名称' })
shopName: string; shopName: string;
@Column({ length: 500, default: '', comment: '店铺Logo' }) @Column({ length: 500, default: '', comment: '店铺Logo' })
@@ -43,20 +43,20 @@ export class Merchant {
@Column({ type: 'decimal', precision: 10, scale: 7, nullable: true, comment: '纬度' }) @Column({ type: 'decimal', precision: 10, scale: 7, nullable: true, comment: '纬度' })
latitude: number; latitude: number;
@Column({ length: 500, default: '', comment: '营业执照图片' }) @Column({ name: 'business_license', length: 500, default: '', comment: '营业执照图片' })
businessLicense: string; businessLicense: string;
@Column({ length: 50, default: '', comment: '营业执照编号' }) @Column({ name: 'license_no', length: 50, default: '', comment: '营业执照编号' })
licenseNo: string; licenseNo: string;
@Column({ length: 50, default: '', comment: '法人姓名' }) @Column({ name: 'legal_person', length: 50, default: '', comment: '法人姓名' })
legalPerson: string; legalPerson: string;
@Index() @Index()
@Column({ type: 'enum', enum: ['pending', 'approved', 'rejected', 'frozen'], default: 'pending', comment: '状态' }) @Column({ type: 'enum', enum: ['pending', 'approved', 'rejected', 'frozen'], default: 'pending', comment: '状态' })
status: 'pending' | 'approved' | 'rejected' | 'frozen'; status: 'pending' | 'approved' | 'rejected' | 'frozen';
@Column({ length: 500, nullable: true, comment: '拒绝原因' }) @Column({ name: 'reject_reason', length: 500, nullable: true, comment: '拒绝原因' })
rejectReason?: string; rejectReason?: string;
@Column({ type: 'decimal', precision: 10, scale: 2, unsigned: true, default: 0, comment: '保证金' }) @Column({ type: 'decimal', precision: 10, scale: 2, unsigned: true, default: 0, comment: '保证金' })
@@ -78,18 +78,18 @@ export class Merchant {
@Column({ type: 'decimal', precision: 2, scale: 1, unsigned: true, default: 5.0, comment: '评分' }) @Column({ type: 'decimal', precision: 2, scale: 1, unsigned: true, default: 5.0, comment: '评分' })
rating: number; rating: number;
@Column({ type: 'int', unsigned: true, default: 0, comment: '评价数' }) @Column({ name: 'review_count', type: 'int', unsigned: true, default: 0, comment: '评价数' })
reviewCount: number; reviewCount: number;
@Column({ type: 'boolean', default: false, comment: '是否自动接单' }) @Column({ name: 'auto_confirm', type: 'boolean', default: false, comment: '是否自动接单' })
autoConfirm: boolean; autoConfirm: boolean;
@Column({ type: 'json', nullable: true, comment: '自动接单规则配置' }) @Column({ name: 'auto_confirm_rules', type: 'json', nullable: true, comment: '自动接单规则配置' })
autoConfirmRules: object; autoConfirmRules: object;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -37,9 +37,9 @@ export class MktActivity {
@Column({ name: 'end_time', type: 'datetime', nullable: true, comment: '活动结束时间' }) @Column({ name: 'end_time', type: 'datetime', nullable: true, comment: '活动结束时间' })
endTime: Date; endTime: Date;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -69,6 +69,6 @@ export class MktCashback {
@JoinColumn({ name: 'order_id' }) @JoinColumn({ name: 'order_id' })
order: Order; order: Order;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
} }
@@ -37,6 +37,6 @@ export class MktInvitation {
@JoinColumn({ name: 'invitee_id' }) @JoinColumn({ name: 'invitee_id' })
invitee: User; invitee: User;
@CreateDateColumn({ comment: '邀请时间' }) @CreateDateColumn({ name: 'created_at', comment: '邀请时间' })
createdAt: Date; createdAt: Date;
} }
@@ -49,9 +49,9 @@ export class MktInviteWithdrawal {
@JoinColumn({ name: 'user_id' }) @JoinColumn({ name: 'user_id' })
user: User; user: User;
@CreateDateColumn({ comment: '申请时间' }) @CreateDateColumn({ name: 'created_at', comment: '申请时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -41,9 +41,9 @@ export class MktUserInviteStats {
@JoinColumn({ name: 'user_id' }) @JoinColumn({ name: 'user_id' })
user: User; user: User;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -26,6 +26,6 @@ export class Notification {
@Column({ type: 'json', nullable: true, comment: '附加数据' }) @Column({ type: 'json', nullable: true, comment: '附加数据' })
extra: object; extra: object;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
} }
+3 -3
View File
@@ -10,7 +10,7 @@ export class Order {
@PrimaryGeneratedColumn({ type: 'bigint', unsigned: true }) @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true })
id: number; id: number;
@Column({ length: 32, unique: true, comment: '订单号' }) @Column({ name: 'order_no', length: 32, unique: true, comment: '订单号' })
orderNo: string; orderNo: string;
@Index() @Index()
@@ -124,9 +124,9 @@ export class Order {
room: Room; room: Room;
@Index() @Index()
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -5,18 +5,18 @@ export class PlatformConfig {
@PrimaryGeneratedColumn({ type: 'bigint', unsigned: true }) @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true })
id: number; id: number;
@Column({ name: 'config_key', length: 50, unique: true, comment: '配置键' }) @Column({ name: 'config_key', length: 100, unique: true, comment: '配置键' })
configKey: string; configKey: string;
@Column({ name: 'config_value', length: 500, comment: '配置值' }) @Column({ name: 'config_value', type: 'text', comment: '配置值' })
configValue: string; configValue: string;
@Column({ length: 200, nullable: true, comment: '配置说明' }) @Column({ length: 200, nullable: true, comment: '配置说明' })
description: string; description: string;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
+4 -7
View File
@@ -46,15 +46,12 @@ export class Review {
@Column({ name: 'is_anonymous', type: 'boolean', default: false, comment: '是否匿名' }) @Column({ name: 'is_anonymous', type: 'boolean', default: false, comment: '是否匿名' })
isAnonymous: boolean; isAnonymous: boolean;
@Column({ name: 'audit_status', type: 'enum', enum: ['pending', 'approved', 'rejected'], default: 'pending', comment: '审核状态' }) @Column({ type: 'enum', enum: ['visible', 'hidden'], default: 'visible', comment: '状态' })
auditStatus: 'pending' | 'approved' | 'rejected'; status: 'visible' | 'hidden';
@Column({ name: 'audit_reject_reason', type: 'varchar', length: 500, nullable: true, comment: '审核拒绝原因' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
auditRejectReason: string;
@CreateDateColumn({ comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -12,18 +12,18 @@ export class RoomCalendarLog {
@Column({ name: 'operator_id', type: 'bigint', unsigned: true, comment: '操作人ID' }) @Column({ name: 'operator_id', type: 'bigint', unsigned: true, comment: '操作人ID' })
operatorId: number; operatorId: number;
@Column({ length: 100, comment: '变更日期范围' }) @Column({ name: 'date_range', length: 100, comment: '变更日期范围' })
dateRange: string; dateRange: string;
@Column({ type: 'enum', enum: ['price', 'stock', 'status'], comment: '变更类型' }) @Column({ name: 'change_type', type: 'enum', enum: ['price', 'stock', 'status'], comment: '变更类型' })
changeType: 'price' | 'stock' | 'status'; changeType: 'price' | 'stock' | 'status';
@Column({ length: 100, nullable: true, comment: '变更前值' }) @Column({ name: 'old_value', length: 100, nullable: true, comment: '变更前值' })
oldValue: string; oldValue: string;
@Column({ length: 100, nullable: true, comment: '变更后值' }) @Column({ name: 'new_value', length: 100, nullable: true, comment: '变更后值' })
newValue: string; newValue: string;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
} }
@@ -29,9 +29,9 @@ export class RoomCalendar {
@Column({ type: 'enum', enum: ['available', 'unavailable'], default: 'available', comment: '房态' }) @Column({ type: 'enum', enum: ['available', 'unavailable'], default: 'available', comment: '房态' })
status: 'available' | 'unavailable'; status: 'available' | 'unavailable';
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
+9 -9
View File
@@ -24,10 +24,10 @@ export class Room {
@Column({ type: 'decimal', precision: 6, scale: 2, default: 0, comment: '面积(平方米)' }) @Column({ type: 'decimal', precision: 6, scale: 2, default: 0, comment: '面积(平方米)' })
area: number; area: number;
@Column({ length: 50, default: '', comment: '床型' }) @Column({ name: 'bed_type', length: 50, default: '', comment: '床型' })
bedType: string; bedType: string;
@Column({ type: 'tinyint', unsigned: true, default: 1, comment: '最多入住人数' }) @Column({ name: 'max_guests', type: 'tinyint', unsigned: true, default: 1, comment: '最多入住人数' })
maxGuests: number; maxGuests: number;
@Column({ length: 20, default: '', comment: '楼层' }) @Column({ length: 20, default: '', comment: '楼层' })
@@ -39,7 +39,7 @@ export class Room {
@Column({ type: 'json', nullable: true, comment: '图片URL列表' }) @Column({ type: 'json', nullable: true, comment: '图片URL列表' })
images: string[]; images: string[];
@Column({ length: 500, default: '', comment: '封面图' }) @Column({ name: 'cover_image', length: 500, default: '', comment: '封面图' })
coverImage: string; coverImage: string;
@Index() @Index()
@@ -50,28 +50,28 @@ export class Room {
status: 'on_sale' | 'off_sale'; status: 'on_sale' | 'off_sale';
@Index() @Index()
@Column({ type: 'enum', enum: ['pending', 'approved', 'rejected'], default: 'pending', comment: '审核状态' }) @Column({ name: 'audit_status', type: 'enum', enum: ['pending', 'approved', 'rejected'], default: 'pending', comment: '审核状态' })
auditStatus: 'pending' | 'approved' | 'rejected'; auditStatus: 'pending' | 'approved' | 'rejected';
@Column({ length: 500, nullable: true, comment: '审核拒绝原因' }) @Column({ name: 'audit_reject_reason', length: 500, nullable: true, comment: '审核拒绝原因' })
auditRejectReason?: string; auditRejectReason?: string;
@Index() @Index()
@Column({ type: 'decimal', precision: 2, scale: 1, unsigned: true, default: 5.0, comment: '评分' }) @Column({ type: 'decimal', precision: 2, scale: 1, unsigned: true, default: 5.0, comment: '评分' })
rating: number; rating: number;
@Column({ type: 'int', unsigned: true, default: 0, comment: '评价数' }) @Column({ name: 'review_count', type: 'int', unsigned: true, default: 0, comment: '评价数' })
reviewCount: number; reviewCount: number;
@Column({ type: 'text', nullable: true, comment: '房源描述' }) @Column({ type: 'text', nullable: true, comment: '房源描述' })
description: string; description: string;
@Column({ type: 'enum', enum: ['free', 'flexible', 'strict'], default: 'flexible', comment: '取消政策' }) @Column({ name: 'cancel_policy', type: 'enum', enum: ['free', 'flexible', 'strict'], default: 'flexible', comment: '取消政策' })
cancelPolicy: 'free' | 'flexible' | 'strict'; cancelPolicy: 'free' | 'flexible' | 'strict';
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
+2 -2
View File
@@ -31,9 +31,9 @@ export class Seller {
@OneToOne(() => Merchant, merchant => merchant.seller) @OneToOne(() => Merchant, merchant => merchant.seller)
merchant: Merchant; merchant: Merchant;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -24,6 +24,6 @@ export class SettlementItem {
@Column({ name: 'order_amount', type: 'decimal', precision: 10, scale: 2, unsigned: true, comment: '订单金额' }) @Column({ name: 'order_amount', type: 'decimal', precision: 10, scale: 2, unsigned: true, comment: '订单金额' })
orderAmount: number; orderAmount: number;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
} }
@@ -56,9 +56,9 @@ export class Settlement {
@OneToMany(() => SettlementItem, item => item.settlement) @OneToMany(() => SettlementItem, item => item.settlement)
items: SettlementItem[]; items: SettlementItem[];
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
+5 -5
View File
@@ -20,22 +20,22 @@ export class User {
@Column({ type: 'tinyint', unsigned: true, default: 0, comment: '性别 0-未知 1-男 2-女' }) @Column({ type: 'tinyint', unsigned: true, default: 0, comment: '性别 0-未知 1-男 2-女' })
gender: number; gender: number;
@Column({ length: 50, nullable: true, comment: '真实姓名' }) @Column({ name: 'real_name', length: 50, nullable: true, comment: '真实姓名' })
realName: string; realName: string;
@Column({ length: 255, nullable: true, select: false, comment: '身份证号' }) @Column({ name: 'id_card', length: 255, nullable: true, select: false, comment: '身份证号' })
idCard: string; idCard: string;
@Index() @Index()
@Column({ type: 'enum', enum: ['active', 'frozen', 'deleted'], default: 'active', comment: '状态' }) @Column({ type: 'enum', enum: ['active', 'frozen', 'deleted'], default: 'active', comment: '状态' })
status: 'active' | 'frozen' | 'deleted'; status: 'active' | 'frozen' | 'deleted';
@Column({ type: 'datetime', nullable: true, comment: '最后登录时间' }) @Column({ name: 'last_login_at', type: 'datetime', nullable: true, comment: '最后登录时间' })
lastLoginAt: Date; lastLoginAt: Date;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -54,9 +54,9 @@ export class Withdrawal {
@Column({ name: 'paid_at', type: 'datetime', nullable: true, comment: '打款时间' }) @Column({ name: 'paid_at', type: 'datetime', nullable: true, comment: '打款时间' })
paidAt: Date; paidAt: Date;
@CreateDateColumn({ comment: '创建时间' }) @CreateDateColumn({ name: 'created_at', comment: '创建时间' })
createdAt: Date; createdAt: Date;
@UpdateDateColumn({ comment: '更新时间' }) @UpdateDateColumn({ name: 'updated_at', comment: '更新时间' })
updatedAt: Date; updatedAt: Date;
} }
@@ -236,11 +236,11 @@ export class ActivityService {
const inviteeIds = list.map((item) => item.inviteeId); const inviteeIds = list.map((item) => item.inviteeId);
const orderCounts = await this.orderRepo const orderCounts = await this.orderRepo
.createQueryBuilder('order') .createQueryBuilder('order')
.select('order.userId', 'userId') .select('order.user_id', 'userId')
.addSelect('COUNT(*)', 'count') .addSelect('COUNT(*)', 'count')
.where('order.userId IN (:...ids)', { ids: inviteeIds }) .where('order.user_id IN (:...ids)', { ids: inviteeIds })
.andWhere('order.status = :status', { status: 'completed' }) .andWhere('order.status = :status', { status: 'completed' })
.groupBy('order.userId') .groupBy('order.user_id')
.getRawMany(); .getRawMany();
const orderCountMap = new Map(orderCounts.map((item) => [item.userId, parseInt(item.count)])); const orderCountMap = new Map(orderCounts.map((item) => [item.userId, parseInt(item.count)]));
@@ -436,11 +436,10 @@ export class ActivityService {
// ===== 管理端功能 ===== // ===== 管理端功能 =====
async getInviteCashbackConfig() { async getInviteCashbackConfig() {
const activity = await this.activityRepo.findOne({ let activity = await this.activityRepo.findOne({
where: { type: 'invite_cashback' }, where: { type: 'invite_cashback' },
}); });
if (!activity) { if (!activity) {
// 创建默认配置
activity = this.activityRepo.create({ activity = this.activityRepo.create({
name: '邀请返现', name: '邀请返现',
type: 'invite_cashback', type: 'invite_cashback',
@@ -24,6 +24,33 @@ export class InviteCashbackConfig {
withdrawThreshold: number; withdrawThreshold: number;
} }
export class UpdateInviteCashbackConfig {
@ApiPropertyOptional({ description: '首单返现比例' })
@IsOptional()
@IsNumber()
firstOrderRate?: number;
@ApiPropertyOptional({ description: '二单返现比例' })
@IsOptional()
@IsNumber()
secondOrderRate?: number;
@ApiPropertyOptional({ description: '最低返现' })
@IsOptional()
@IsNumber()
minCashback?: number;
@ApiPropertyOptional({ description: '最高返现' })
@IsOptional()
@IsNumber()
maxCashback?: number;
@ApiPropertyOptional({ description: '提现门槛' })
@IsOptional()
@IsNumber()
withdrawThreshold?: number;
}
// ===== 活动 DTO ===== // ===== 活动 DTO =====
export class CreateActivityDto { export class CreateActivityDto {
@@ -63,12 +90,17 @@ export class UpdateActivityDto {
@IsString() @IsString()
name?: string; name?: string;
@ApiPropertyOptional({ description: '是否启用' })
@IsOptional()
@IsBoolean()
enabled?: boolean;
@ApiPropertyOptional({ description: '活动配置' }) @ApiPropertyOptional({ description: '活动配置' })
@IsOptional() @IsOptional()
@IsObject() @IsObject()
@ValidateNested() @ValidateNested()
@Type(() => InviteCashbackConfig) @Type(() => UpdateInviteCashbackConfig)
config?: InviteCashbackConfig; config?: UpdateInviteCashbackConfig;
@ApiPropertyOptional({ description: '活动开始时间' }) @ApiPropertyOptional({ description: '活动开始时间' })
@IsOptional() @IsOptional()
@@ -10,4 +10,7 @@ import { ConfigController } from './config.controller';
providers: [ConfigService], providers: [ConfigService],
exports: [ConfigService], exports: [ConfigService],
}) })
export class ConfigModule {} export class ConfigModule {}
// 别名导出,保持兼容性
export { ConfigModule as PlatformConfigModule };
@@ -63,7 +63,7 @@ export class MerchantService {
.where('m.status = :status', { status: 'approved' }); .where('m.status = :status', { status: 'approved' });
if (keyword) { if (keyword) {
qb.andWhere('m.shopName LIKE :kw', { kw: `%${keyword}%` }); qb.andWhere('m.shop_name LIKE :kw', { kw: `%${keyword}%` });
} }
if (city) { if (city) {
qb.andWhere('m.city = :city', { city }); qb.andWhere('m.city = :city', { city });
@@ -87,7 +87,7 @@ export class MerchantService {
const qb = this.merchantRepo.createQueryBuilder('m'); const qb = this.merchantRepo.createQueryBuilder('m');
if (keyword) { if (keyword) {
qb.andWhere('(m.shopName LIKE :kw OR m.phone LIKE :kw)', { qb.andWhere('(m.shop_name LIKE :kw OR m.phone LIKE :kw)', {
kw: `%${keyword}%`, kw: `%${keyword}%`,
}); });
} }
@@ -3,6 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { Order } from '@/entities/order.entity'; import { Order } from '@/entities/order.entity';
import { Room } from '@/entities/room.entity'; import { Room } from '@/entities/room.entity';
import { RoomCalendar } from '@/entities/room-calendar.entity'; import { RoomCalendar } from '@/entities/room-calendar.entity';
import { User } from '@/entities/user.entity';
import { Merchant } from '@/entities/merchant.entity';
import { OrderService } from './order.service'; import { OrderService } from './order.service';
import { UserOrderController, MerchantOrderController, AdminOrderController } from './order.controller'; import { UserOrderController, MerchantOrderController, AdminOrderController } from './order.controller';
import { MerchantModule } from '../merchant/merchant.module'; import { MerchantModule } from '../merchant/merchant.module';
@@ -11,7 +13,7 @@ import { PlatformConfigModule } from '../config/config.module';
@Module({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([Order, Room, RoomCalendar]), TypeOrmModule.forFeature([Order, Room, RoomCalendar, User, Merchant]),
MerchantModule, MerchantModule,
forwardRef(() => ActivityModule), forwardRef(() => ActivityModule),
PlatformConfigModule, PlatformConfigModule,
@@ -86,7 +86,7 @@ export class OrderService {
const qb = this.orderRepo.createQueryBuilder('o') const qb = this.orderRepo.createQueryBuilder('o')
.leftJoinAndSelect('o.room', 'r') .leftJoinAndSelect('o.room', 'r')
.leftJoinAndSelect('o.merchant', 'm') .leftJoinAndSelect('o.merchant', 'm')
.where('o.userId = :userId', { userId }); .where('o.user_id = :userId', { userId });
if (status) { if (status) {
qb.andWhere('o.status = :status', { status }); qb.andWhere('o.status = :status', { status });
@@ -104,12 +104,12 @@ export class OrderService {
const qb = this.orderRepo.createQueryBuilder('o') const qb = this.orderRepo.createQueryBuilder('o')
.leftJoinAndSelect('o.room', 'r') .leftJoinAndSelect('o.room', 'r')
.leftJoinAndSelect('o.user', 'u') .leftJoinAndSelect('o.user', 'u')
.where('o.merchantId = :merchantId', { merchantId }); .where('o.merchant_id = :merchantId', { merchantId });
if (status) qb.andWhere('o.status = :status', { status }); if (status) qb.andWhere('o.status = :status', { status });
if (orderNo) qb.andWhere('o.orderNo LIKE :orderNo', { orderNo: `%${orderNo}%` }); if (orderNo) qb.andWhere('o.order_no LIKE :orderNo', { orderNo: `%${orderNo}%` });
if (startDate) qb.andWhere('o.createdAt >= :startDate', { startDate }); if (startDate) qb.andWhere('o.created_at >= :startDate', { startDate });
if (endDate) qb.andWhere('o.createdAt <= :endDate', { endDate }); if (endDate) qb.andWhere('o.created_at <= :endDate', { endDate });
qb.orderBy('o.createdAt', 'DESC'); qb.orderBy('o.createdAt', 'DESC');
qb.skip((page - 1) * pageSize).take(pageSize); qb.skip((page - 1) * pageSize).take(pageSize);
@@ -349,9 +349,9 @@ export class OrderService {
.leftJoinAndSelect('o.user', 'u'); .leftJoinAndSelect('o.user', 'u');
if (status) qb.andWhere('o.status = :status', { status }); if (status) qb.andWhere('o.status = :status', { status });
if (orderNo) qb.andWhere('o.orderNo LIKE :orderNo', { orderNo: `%${orderNo}%` }); if (orderNo) qb.andWhere('o.order_no LIKE :orderNo', { orderNo: `%${orderNo}%` });
if (startDate) qb.andWhere('o.createdAt >= :startDate', { startDate }); if (startDate) qb.andWhere('o.created_at >= :startDate', { startDate });
if (endDate) qb.andWhere('o.createdAt <= :endDate', { endDate }); if (endDate) qb.andWhere('o.created_at <= :endDate', { endDate });
qb.orderBy('o.createdAt', 'DESC'); qb.orderBy('o.createdAt', 'DESC');
qb.skip((page - 1) * pageSize).take(pageSize); qb.skip((page - 1) * pageSize).take(pageSize);
@@ -47,26 +47,26 @@ export class ReviewService {
content: dto.content, content: dto.content,
images: dto.images, images: dto.images,
isAnonymous: dto.isAnonymous || false, isAnonymous: dto.isAnonymous || false,
auditStatus: 'pending', status: 'visible',
}); });
await this.reviewRepo.save(review); await this.reviewRepo.save(review);
return { message: '评价已提交,等待审核' }; return { message: '评价已提交' };
} }
// 查询评价列表(公开,只显示审核通过的) // 查询评价列表(公开,只显示可见的)
async findPublic(query: QueryReviewDto) { async findPublic(query: QueryReviewDto) {
const { page = 1, pageSize = 10, merchantId, roomId } = query; const { page = 1, pageSize = 10, merchantId, roomId } = query;
const qb = this.reviewRepo const qb = this.reviewRepo
.createQueryBuilder('r') .createQueryBuilder('r')
.leftJoinAndSelect('r.order', 'o') .leftJoinAndSelect('r.order', 'o')
.where('r.auditStatus = :auditStatus', { auditStatus: 'approved' }); .where('r.status = :status', { status: 'visible' });
if (merchantId) { if (merchantId) {
qb.andWhere('r.merchantId = :merchantId', { merchantId: Number(merchantId) }); qb.andWhere('r.merchant_id = :merchantId', { merchantId: Number(merchantId) });
} }
if (roomId) { if (roomId) {
qb.andWhere('r.roomId = :roomId', { roomId: Number(roomId) }); qb.andWhere('r.room_id = :roomId', { roomId: Number(roomId) });
} }
qb.orderBy('r.createdAt', 'DESC'); qb.orderBy('r.createdAt', 'DESC');
@@ -83,7 +83,7 @@ export class ReviewService {
return this.reviewRepo.findOne({ where: { orderId } }); return this.reviewRepo.findOne({ where: { orderId } });
} }
// 管理员审核评价列表 // 管理员评价列表
async findAllForAdmin(query: QueryReviewDto) { async findAllForAdmin(query: QueryReviewDto) {
const { page = 1, pageSize = 10, auditStatus } = query; const { page = 1, pageSize = 10, auditStatus } = query;
const qb = this.reviewRepo const qb = this.reviewRepo
@@ -91,7 +91,7 @@ export class ReviewService {
.leftJoinAndSelect('r.order', 'o'); .leftJoinAndSelect('r.order', 'o');
if (auditStatus) { if (auditStatus) {
qb.andWhere('r.auditStatus = :auditStatus', { auditStatus }); qb.andWhere('r.status = :status', { status: auditStatus });
} }
qb.orderBy('r.createdAt', 'DESC'); qb.orderBy('r.createdAt', 'DESC');
@@ -103,27 +103,27 @@ export class ReviewService {
return { list, total, page: safePage, pageSize: safePageSize, totalPages: Math.ceil(total / safePageSize) }; return { list, total, page: safePage, pageSize: safePageSize, totalPages: Math.ceil(total / safePageSize) };
} }
// 审核通过 // 显示评价
async approve(id: number) { async approve(id: number) {
const review = await this.reviewRepo.findOne({ where: { id } }); const review = await this.reviewRepo.findOne({ where: { id } });
if (!review) throw new NotFoundException('评价不存在'); if (!review) throw new NotFoundException('评价不存在');
await this.reviewRepo.update(id, { auditStatus: 'approved', auditRejectReason: '' }); await this.reviewRepo.update(id, { status: 'visible' });
// 更新商家和房源的评分 // 更新商家和房源的评分
await this.updateMerchantRating(review.merchantId); await this.updateMerchantRating(review.merchantId);
await this.updateRoomRating(review.roomId); await this.updateRoomRating(review.roomId);
return { message: '评价已通过审核' }; return { message: '评价已显示' };
} }
// 审核拒绝 // 隐藏评价
async reject(id: number, reason: string) { async reject(id: number, _reason: string) {
const review = await this.reviewRepo.findOne({ where: { id } }); const review = await this.reviewRepo.findOne({ where: { id } });
if (!review) throw new NotFoundException('评价不存在'); if (!review) throw new NotFoundException('评价不存在');
await this.reviewRepo.update(id, { auditStatus: 'rejected', auditRejectReason: reason }); await this.reviewRepo.update(id, { status: 'hidden' });
return { message: '评价已拒绝' }; return { message: '评价已隐藏' };
} }
// 更新商家平均评分 // 更新商家平均评分
@@ -132,8 +132,8 @@ export class ReviewService {
.createQueryBuilder('r') .createQueryBuilder('r')
.select('AVG(r.rating)', 'avgRating') .select('AVG(r.rating)', 'avgRating')
.addSelect('COUNT(r.id)', 'count') .addSelect('COUNT(r.id)', 'count')
.where('r.merchantId = :merchantId', { merchantId }) .where('r.merchant_id = :merchantId', { merchantId })
.andWhere('r.auditStatus = :auditStatus', { auditStatus: 'approved' }) .andWhere('r.status = :status', { status: 'visible' })
.getRawOne(); .getRawOne();
if (result) { if (result) {
@@ -150,8 +150,8 @@ export class ReviewService {
.createQueryBuilder('r') .createQueryBuilder('r')
.select('AVG(r.rating)', 'avgRating') .select('AVG(r.rating)', 'avgRating')
.addSelect('COUNT(r.id)', 'count') .addSelect('COUNT(r.id)', 'count')
.where('r.roomId = :roomId', { roomId }) .where('r.room_id = :roomId', { roomId })
.andWhere('r.auditStatus = :auditStatus', { auditStatus: 'approved' }) .andWhere('r.status = :status', { status: 'visible' })
.getRawOne(); .getRawOne();
if (result) { if (result) {
+6 -6
View File
@@ -90,7 +90,7 @@ export class RoomService {
.createQueryBuilder('r') .createQueryBuilder('r')
.leftJoinAndSelect('r.merchant', 'm') .leftJoinAndSelect('r.merchant', 'm')
.where('r.status = :status', { status: 'on_sale' }) .where('r.status = :status', { status: 'on_sale' })
.andWhere('r.auditStatus = :auditStatus', { auditStatus: 'approved' }); .andWhere('r.audit_status = :auditStatus', { auditStatus: 'approved' });
if (keyword) { if (keyword) {
qb.andWhere('r.name LIKE :kw', { kw: `%${keyword}%` }); qb.andWhere('r.name LIKE :kw', { kw: `%${keyword}%` });
@@ -108,15 +108,15 @@ export class RoomService {
qb.andWhere('m.city = :city', { city }); qb.andWhere('m.city = :city', { city });
} }
if (merchantId) { if (merchantId) {
qb.andWhere('r.merchantId = :merchantId', { merchantId: Number(merchantId) }); qb.andWhere('r.merchant_id = :merchantId', { merchantId: Number(merchantId) });
} }
// 人数筛选:房间可住人数 >= 成人数 // 人数筛选:房间可住人数 >= 成人数
if (adultCount && !roomCount) { if (adultCount && !roomCount) {
qb.andWhere('r.maxGuests >= :adultCount', { adultCount }); qb.andWhere('r.max_guests >= :adultCount', { adultCount });
} else if (adultCount && roomCount) { } else if (adultCount && roomCount) {
// 多间房时,总可住人数 >= 成人数 // 多间房时,总可住人数 >= 成人数
qb.andWhere('r.maxGuests * :roomCount >= :adultCount', { roomCount, adultCount }); qb.andWhere('r.max_guests * :roomCount >= :adultCount', { roomCount, adultCount });
} }
const sortMap: Record<string, string> = { const sortMap: Record<string, string> = {
@@ -203,7 +203,7 @@ export class RoomService {
} }
const qb = this.roomRepo const qb = this.roomRepo
.createQueryBuilder('r') .createQueryBuilder('r')
.where('r.merchantId = :merchantId', { merchantId: safeMerchantId }); .where('r.merchant_id = :merchantId', { merchantId: safeMerchantId });
if (status) { if (status) {
qb.andWhere('r.status = :status', { status }); qb.andWhere('r.status = :status', { status });
@@ -232,7 +232,7 @@ export class RoomService {
qb.andWhere('r.name LIKE :kw', { kw: `%${keyword}%` }); qb.andWhere('r.name LIKE :kw', { kw: `%${keyword}%` });
} }
if (auditStatus) { if (auditStatus) {
qb.andWhere('r.auditStatus = :auditStatus', { auditStatus }); qb.andWhere('r.audit_status = :auditStatus', { auditStatus });
} }
if (type) { if (type) {
qb.andWhere('r.type = :type', { type }); qb.andWhere('r.type = :type', { type });
+9654 -7800
View File
File diff suppressed because it is too large Load Diff