This commit is contained in:
2026-05-14 09:55:31 +08:00
16 changed files with 88 additions and 73 deletions
+5 -1
View File
@@ -32,7 +32,11 @@
"Bash(python3 -c \"import sys, json; data=json.load\\(sys.stdin\\); token=data['data']['accessToken']; import jwt; decoded=jwt.decode\\(token, options={'verify_signature': False}\\); print\\(f\\\\\"iat: {decoded['iat']}, exp: {decoded['exp']}, duration: {decoded['exp']-decoded['iat']} seconds \\({\\(decoded['exp']-decoded['iat']\\)/3600} hours\\)\\\\\"\\)\")", "Bash(python3 -c \"import sys, json; data=json.load\\(sys.stdin\\); token=data['data']['accessToken']; import jwt; decoded=jwt.decode\\(token, options={'verify_signature': False}\\); print\\(f\\\\\"iat: {decoded['iat']}, exp: {decoded['exp']}, duration: {decoded['exp']-decoded['iat']} seconds \\({\\(decoded['exp']-decoded['iat']\\)/3600} hours\\)\\\\\"\\)\")",
"Bash(curl -s -X GET http://localhost:3000/api/orders -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwicGhvbmUiOiIxMzgwMDEzODAwMCIsImlhdCI6MTc3ODM4MTc2NywiZXhwIjoxNzc4Mzg4OTY3fQ.-jcY69afC6O6CtKtOj6tRaoiknypwDfvpXYhpz95rVE\")", "Bash(curl -s -X GET http://localhost:3000/api/orders -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwicGhvbmUiOiIxMzgwMDEzODAwMCIsImlhdCI6MTc3ODM4MTc2NywiZXhwIjoxNzc4Mzg4OTY3fQ.-jcY69afC6O6CtKtOj6tRaoiknypwDfvpXYhpz95rVE\")",
"Bash(pnpm install *)", "Bash(pnpm install *)",
"Bash(pnpm dev:mp-weixin)" "Bash(pnpm dev:mp-weixin)",
"Read(//e/project/wb/**)",
"Bash(pnpm dev:server)",
"Bash(pnpm --filter @rent/server run build)",
"Bash(pnpm --filter @rent/server run dev)"
] ]
} }
} }
+4 -4
View File
@@ -1,16 +1,16 @@
import request from '@/utils/request'; import { get, post } from '@/utils/request';
// 获取可领取的优惠券列表 // 获取可领取的优惠券列表
export function getAvailableCoupons(params?: { merchantId?: number; roomId?: number }) { export function getAvailableCoupons(params?: { merchantId?: number; roomId?: number }) {
return request.get('/user/coupons/available', params); return get('/user/coupons/available', params);
} }
// 领取优惠券 // 领取优惠券
export function claimCoupon(couponId: number) { export function claimCoupon(couponId: number) {
return request.post('/user/coupons/claim', { couponId }); return post('/user/coupons/claim', { couponId });
} }
// 获取我的优惠券列表 // 获取我的优惠券列表
export function getMyCoupons(params?: { status?: string }) { export function getMyCoupons(params?: { status?: string }) {
return request.get('/user/coupons', params); return get('/user/coupons', params);
} }
+7 -7
View File
@@ -1,31 +1,31 @@
import request from '@/utils/request'; import { get, post, put, del } from '@/utils/request';
// 获取常住人列表 // 获取常住人列表
export function getGuestList() { export function getGuestList() {
return request.get('/user/guests'); return get('/user/guests');
} }
// 获取常住人详情 // 获取常住人详情
export function getGuestDetail(id: number) { export function getGuestDetail(id: number) {
return request.get(`/user/guests/${id}`); return get(`/user/guests/${id}`);
} }
// 添加常住人 // 添加常住人
export function createGuest(data: any) { export function createGuest(data: any) {
return request.post('/user/guests', data); return post('/user/guests', data);
} }
// 更新常住人 // 更新常住人
export function updateGuest(id: number, data: any) { export function updateGuest(id: number, data: any) {
return request.put(`/user/guests/${id}`, data); return put(`/user/guests/${id}`, data);
} }
// 删除常住人 // 删除常住人
export function deleteGuest(id: number) { export function deleteGuest(id: number) {
return request.delete(`/user/guests/${id}`); return del(`/user/guests/${id}`);
} }
// 设置默认常住人 // 设置默认常住人
export function setDefaultGuest(id: number) { export function setDefaultGuest(id: number) {
return request.put(`/user/guests/${id}/default`); return put(`/user/guests/${id}/default`);
} }
+11 -11
View File
@@ -1,4 +1,4 @@
import request from '@/utils/request'; import { get, post } from '@/utils/request';
/** /**
* 商家财务API * 商家财务API
@@ -7,28 +7,28 @@ export const financeApi = {
/** /**
* 获取钱包信息 * 获取钱包信息
*/ */
getWallet: () => request.get('/seller/finance/wallet'), getWallet: () => get('/seller/finance/wallet'),
/** /**
* 获取交易流水 * 获取交易流水
*/ */
getTransactions: (params?: any) => request.get('/seller/finance/transactions', { params }), getTransactions: (params?: any) => get('/seller/finance/transactions', params),
/** /**
* 获取结算记录 * 获取结算记录
*/ */
getSettlements: (params?: any) => request.get('/seller/finance/settlements', { params }), getSettlements: (params?: any) => get('/seller/finance/settlements', params),
/** /**
* 获取结算详情 * 获取结算详情
*/ */
getSettlementDetail: (id: number) => request.get(`/seller/finance/settlements/${id}`), getSettlementDetail: (id: number) => get(`/seller/finance/settlements/${id}`),
/** /**
* 获取结算明细 * 获取结算明细
*/ */
getSettlementItems: (id: number, params?: any) => getSettlementItems: (id: number, params?: any) =>
request.get(`/seller/finance/settlements/${id}/items`, { params }), get(`/seller/finance/settlements/${id}/items`, params),
/** /**
* 申请提现 * 申请提现
@@ -39,17 +39,17 @@ export const financeApi = {
accountName: string; accountName: string;
accountNumber: string; accountNumber: string;
bankName?: string; bankName?: string;
}) => request.post('/seller/finance/withdrawals', data), }) => post('/seller/finance/withdrawals', data),
/** /**
* 获取提现记录 * 获取提现记录
*/ */
getWithdrawals: (params?: any) => request.get('/seller/finance/withdrawals', { params }), getWithdrawals: (params?: any) => get('/seller/finance/withdrawals', params),
/** /**
* 获取提现详情 * 获取提现详情
*/ */
getWithdrawalDetail: (id: number) => request.get(`/seller/finance/withdrawals/${id}`), getWithdrawalDetail: (id: number) => get(`/seller/finance/withdrawals/${id}`),
}; };
/** /**
@@ -59,11 +59,11 @@ export const statisticsApi = {
/** /**
* 获取数据概览 * 获取数据概览
*/ */
getOverview: () => request.get('/seller/statistics/overview'), getOverview: () => get('/seller/statistics/overview'),
/** /**
* 获取收入趋势 * 获取收入趋势
*/ */
getIncomeTrend: (type: 'day' | 'week' | 'month' = 'day') => getIncomeTrend: (type: 'day' | 'week' | 'month' = 'day') =>
request.get('/seller/statistics/income-trend', { params: { type } }), get('/seller/statistics/income-trend', { type }),
}; };
+3 -3
View File
@@ -1,4 +1,4 @@
import request from '@/utils/request'; import { request } from '@/utils/request';
// 钱包信息接口 // 钱包信息接口
export interface WalletInfo { export interface WalletInfo {
@@ -58,7 +58,7 @@ export const walletApi = {
return request<{ items: Transaction[]; total: number }>({ return request<{ items: Transaction[]; total: number }>({
url: '/user/finance/transactions', url: '/user/finance/transactions',
method: 'GET', method: 'GET',
params, data: params,
}); });
}, },
@@ -85,7 +85,7 @@ export const walletApi = {
return request<{ items: Withdrawal[]; total: number }>({ return request<{ items: Withdrawal[]; total: number }>({
url: '/user/finance/withdrawals', url: '/user/finance/withdrawals',
method: 'GET', method: 'GET',
params, data: params,
}); });
}, },
}; };
+5 -5
View File
@@ -1,21 +1,21 @@
import request from '@/utils/request'; import request from '@/utils/request';
export function getCouponList(params: any) { export function getCouponList(params: any) {
return request.get('/admin/coupons', { params }); return request({ url: '/admin/coupons', method: 'GET', params });
} }
export function getCouponDetail(id: number) { export function getCouponDetail(id: number) {
return request.get(`/admin/coupons/${id}`); return request({ url: `/admin/coupons/${id}`, method: 'GET' });
} }
export function createCoupon(data: any) { export function createCoupon(data: any) {
return request.post('/admin/coupons', data); return request({ url: '/admin/coupons', method: 'POST', data });
} }
export function updateCoupon(id: number, data: any) { export function updateCoupon(id: number, data: any) {
return request.put(`/admin/coupons/${id}`, data); return request({ url: `/admin/coupons/${id}`, method: 'PUT', data });
} }
export function deleteCoupon(id: number) { export function deleteCoupon(id: number) {
return request.delete(`/admin/coupons/${id}`); return request({ url: `/admin/coupons/${id}`, method: 'DELETE' });
} }
+1 -1
View File
@@ -54,7 +54,7 @@ export class Order {
couponDiscount: number; couponDiscount: number;
@Column({ name: 'user_coupon_id', type: 'bigint', unsigned: true, nullable: true, comment: '使用的用户优惠券ID' }) @Column({ name: 'user_coupon_id', type: 'bigint', unsigned: true, nullable: true, comment: '使用的用户优惠券ID' })
userCouponId: number; userCouponId: number | null;
@Column({ name: 'total_amount', type: 'decimal', precision: 10, scale: 2, unsigned: true, comment: '订单总金额' }) @Column({ name: 'total_amount', type: 'decimal', precision: 10, scale: 2, unsigned: true, comment: '订单总金额' })
totalAmount: number; totalAmount: number;
@@ -0,0 +1,7 @@
// 统一导出所有财务相关 DTO
export * from './withdrawal.dto';
export * from './transaction.dto';
export * from './account.dto';
export * from './settlement.dto';
export * from './reconciliation.dto';
export * from './report.dto';
@@ -5,11 +5,11 @@ import { ConfigService } from '@nestjs/config';
import { Order } from '@/entities/order.entity'; import { Order } from '@/entities/order.entity';
import { PlatformAccount } from '@/entities/platform-account.entity'; import { PlatformAccount } from '@/entities/platform-account.entity';
import { PlatformTransaction } from '@/entities/platform-transaction.entity'; import { PlatformTransaction } from '@/entities/platform-transaction.entity';
import { Wechatpay } from 'wechatpay-node-v3'; import Wechatpay = require('wechatpay-node-v3');
@Injectable() @Injectable()
export class RefundService { export class RefundService {
private wechatpayClient: Wechatpay; private wechatpayClient: Wechatpay | null;
constructor( constructor(
@InjectRepository(Order) @InjectRepository(Order)
@@ -45,9 +45,10 @@ export class RefundService {
this.wechatpayClient = new Wechatpay({ this.wechatpayClient = new Wechatpay({
appid, appid,
mchid, mchid,
publicKey: Buffer.from(privateKey, 'utf-8'),
privateKey: Buffer.from(privateKey, 'utf-8'), privateKey: Buffer.from(privateKey, 'utf-8'),
serialNo, serial_no: serialNo,
apiv3Key, key: apiv3Key,
}); });
console.log('[微信支付] 客户端初始化成功'); console.log('[微信支付] 客户端初始化成功');
} catch (error) { } catch (error) {
@@ -153,7 +154,7 @@ export class RefundService {
const totalAmount = Math.round(order.payAmount * 100); const totalAmount = Math.round(order.payAmount * 100);
// 调用微信支付退款API // 调用微信支付退款API
const result = await this.wechatpayClient.refunds({ const result: any = await this.wechatpayClient.refunds({
transaction_id: order.transactionId, transaction_id: order.transactionId,
out_refund_no: refundNo, out_refund_no: refundNo,
reason: '用户申请退款', reason: '用户申请退款',
@@ -11,7 +11,7 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { GuestService } from './guest.service'; import { GuestService } from './guest.service';
import { CreateGuestDto, UpdateGuestDto } from './dto/guest.dto'; import { CreateGuestDto, UpdateGuestDto } from './dto/guest.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard';
@Controller('user/guests') @Controller('user/guests')
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@@ -1,9 +1,9 @@
import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { Controller, Get, Query, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '@/modules/auth/jwt-auth.guard'; import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard';
import { RolesGuard } from '@/modules/auth/roles.guard'; import { RolesGuard } from '@/common/guards/roles.guard';
import { Roles } from '@/modules/auth/roles.decorator'; import { Roles } from '@/common/decorators/roles.decorator';
import { CurrentUser } from '@/modules/auth/current-user.decorator'; import { CurrentUser } from '@/common/decorators/current-user.decorator';
import { StatisticsService } from './statistics.service'; import { StatisticsService } from './statistics.service';
@ApiTags('商家统计') @ApiTags('商家统计')
@@ -63,7 +63,7 @@ export class StatisticsService {
month: monthData, month: monthData,
roomCount, roomCount,
balance: account?.balance || 0, balance: account?.balance || 0,
availableBalance: account?.availableBalance || 0, availableBalance: account ? Number(account.balance) - Number(account.frozenBalance) : 0,
}; };
} }
@@ -82,7 +82,7 @@ export class OrderService {
// 处理优惠券 // 处理优惠券
let couponDiscount = 0; let couponDiscount = 0;
let userCouponId = null; let userCouponId: number | null = null;
if (dto.couponId) { if (dto.couponId) {
// 查询用户可用优惠券 // 查询用户可用优惠券
@@ -138,7 +138,7 @@ export class OrderService {
serviceFee, serviceFee,
merchantIncome, merchantIncome,
couponDiscount, couponDiscount,
userCouponId, userCouponId: userCouponId !== null ? userCouponId : undefined,
totalAmount, totalAmount,
payAmount, payAmount,
contactName: dto.contactName, contactName: dto.contactName,
@@ -0,0 +1,25 @@
import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { SettlementService } from '@/modules/finance/settlement.service';
@Injectable()
export class SettlementSchedule {
private readonly logger = new Logger(SettlementSchedule.name);
constructor(
private readonly settlementService: SettlementService,
) {}
// 每天凌晨2点执行自动结算
@Cron('0 2 * * *')
async autoSettle() {
try {
this.logger.log('开始执行自动结算任务');
// 调用 SettlementService 的周结算方法
await this.settlementService.handleWeeklySettlement();
this.logger.log('自动结算任务执行完成');
} catch (error) {
this.logger.error('自动结算任务执行失败', error.stack);
}
}
}
+6 -2
View File
@@ -10,8 +10,10 @@ USE `rent_platform`;
-- ============================================================ -- ============================================================
CREATE TABLE `users` ( CREATE TABLE `users` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`phone` VARCHAR(20) NOT NULL COMMENT '手机号', `phone` VARCHAR(20) NULL COMMENT '手机号',
`password` VARCHAR(255) DEFAULT NULL COMMENT '密码(AES加密存储)', `password` VARCHAR(255) DEFAULT NULL COMMENT '密码(AES加密存储)',
`wechat_openid` VARCHAR(100) NULL COMMENT '微信openid',
`wechat_unionid` VARCHAR(100) NULL COMMENT '微信unionid',
`nickname` VARCHAR(50) DEFAULT '' COMMENT '昵称', `nickname` VARCHAR(50) DEFAULT '' COMMENT '昵称',
`avatar` VARCHAR(500) DEFAULT '' COMMENT '头像URL', `avatar` VARCHAR(500) DEFAULT '' COMMENT '头像URL',
`gender` TINYINT UNSIGNED DEFAULT 0 COMMENT '性别 0-未知 1-男 2-女', `gender` TINYINT UNSIGNED DEFAULT 0 COMMENT '性别 0-未知 1-男 2-女',
@@ -22,7 +24,9 @@ CREATE TABLE `users` (
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uk_phone` (`phone`), KEY `idx_phone` (`phone`),
KEY `idx_wechat_openid` (`wechat_openid`),
KEY `idx_wechat_unionid` (`wechat_unionid`),
KEY `idx_status` (`status`), KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`) KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
@@ -1,26 +0,0 @@
-- 添加微信登录相关字段
-- 执行时间: 2026-05-13
USE rent_platform;
-- 修改 users 表,添加微信相关字段
ALTER TABLE `users`
-- 手机号改为可空,因为微信登录时可能没有手机号
MODIFY COLUMN `phone` VARCHAR(20) NULL COMMENT '手机号',
-- 删除手机号的唯一索引
DROP INDEX `phone`,
-- 添加手机号的普通索引
ADD INDEX `idx_phone` (`phone`),
-- 添加微信 openid 字段
ADD COLUMN `wechat_openid` VARCHAR(100) NULL COMMENT '微信openid' AFTER `password`,
-- 添加微信 unionid 字段
ADD COLUMN `wechat_unionid` VARCHAR(100) NULL COMMENT '微信unionid' AFTER `wechat_openid`,
-- 添加 openid 索引
ADD INDEX `idx_wechat_openid` (`wechat_openid`),
-- 添加 unionid 索引
ADD INDEX `idx_wechat_unionid` (`wechat_unionid`);
-- 说明:
-- 1. phone 字段改为可空,支持微信登录时无手机号的情况
-- 2. 添加 wechat_openid 和 wechat_unionid 字段用于微信登录
-- 3. 为这些字段添加索引以提高查询性能