diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8e9bd41..dfbc371 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -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(curl -s -X GET http://localhost:3000/api/orders -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwicGhvbmUiOiIxMzgwMDEzODAwMCIsImlhdCI6MTc3ODM4MTc2NywiZXhwIjoxNzc4Mzg4OTY3fQ.-jcY69afC6O6CtKtOj6tRaoiknypwDfvpXYhpz95rVE\")", "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)" ] } } diff --git a/apps/miniapp/src/api/coupon.ts b/apps/miniapp/src/api/coupon.ts index a66c8d2..4614e98 100644 --- a/apps/miniapp/src/api/coupon.ts +++ b/apps/miniapp/src/api/coupon.ts @@ -1,16 +1,16 @@ -import request from '@/utils/request'; +import { get, post } from '@/utils/request'; // 获取可领取的优惠券列表 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) { - return request.post('/user/coupons/claim', { couponId }); + return post('/user/coupons/claim', { couponId }); } // 获取我的优惠券列表 export function getMyCoupons(params?: { status?: string }) { - return request.get('/user/coupons', params); + return get('/user/coupons', params); } diff --git a/apps/miniapp/src/api/guest.ts b/apps/miniapp/src/api/guest.ts index 88106db..53f4155 100644 --- a/apps/miniapp/src/api/guest.ts +++ b/apps/miniapp/src/api/guest.ts @@ -1,31 +1,31 @@ -import request from '@/utils/request'; +import { get, post, put, del } from '@/utils/request'; // 获取常住人列表 export function getGuestList() { - return request.get('/user/guests'); + return get('/user/guests'); } // 获取常住人详情 export function getGuestDetail(id: number) { - return request.get(`/user/guests/${id}`); + return get(`/user/guests/${id}`); } // 添加常住人 export function createGuest(data: any) { - return request.post('/user/guests', data); + return post('/user/guests', data); } // 更新常住人 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) { - return request.delete(`/user/guests/${id}`); + return del(`/user/guests/${id}`); } // 设置默认常住人 export function setDefaultGuest(id: number) { - return request.put(`/user/guests/${id}/default`); + return put(`/user/guests/${id}/default`); } diff --git a/apps/miniapp/src/api/seller/finance.ts b/apps/miniapp/src/api/seller/finance.ts index ffb23fa..f2213a9 100644 --- a/apps/miniapp/src/api/seller/finance.ts +++ b/apps/miniapp/src/api/seller/finance.ts @@ -1,4 +1,4 @@ -import request from '@/utils/request'; +import { get, post } from '@/utils/request'; /** * 商家财务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) => - request.get(`/seller/finance/settlements/${id}/items`, { params }), + get(`/seller/finance/settlements/${id}/items`, params), /** * 申请提现 @@ -39,17 +39,17 @@ export const financeApi = { accountName: string; accountNumber: 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') => - request.get('/seller/statistics/income-trend', { params: { type } }), + get('/seller/statistics/income-trend', { type }), }; diff --git a/apps/miniapp/src/api/user/wallet.ts b/apps/miniapp/src/api/user/wallet.ts index 2ab3d2d..d31396a 100644 --- a/apps/miniapp/src/api/user/wallet.ts +++ b/apps/miniapp/src/api/user/wallet.ts @@ -1,4 +1,4 @@ -import request from '@/utils/request'; +import { request } from '@/utils/request'; // 钱包信息接口 export interface WalletInfo { @@ -58,7 +58,7 @@ export const walletApi = { return request<{ items: Transaction[]; total: number }>({ url: '/user/finance/transactions', method: 'GET', - params, + data: params, }); }, @@ -85,7 +85,7 @@ export const walletApi = { return request<{ items: Withdrawal[]; total: number }>({ url: '/user/finance/withdrawals', method: 'GET', - params, + data: params, }); }, }; diff --git a/apps/platform-admin/src/api/coupon.ts b/apps/platform-admin/src/api/coupon.ts index c6b2ec0..2522ad3 100644 --- a/apps/platform-admin/src/api/coupon.ts +++ b/apps/platform-admin/src/api/coupon.ts @@ -1,21 +1,21 @@ import request from '@/utils/request'; export function getCouponList(params: any) { - return request.get('/admin/coupons', { params }); + return request({ url: '/admin/coupons', method: 'GET', params }); } export function getCouponDetail(id: number) { - return request.get(`/admin/coupons/${id}`); + return request({ url: `/admin/coupons/${id}`, method: 'GET' }); } 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) { - return request.put(`/admin/coupons/${id}`, data); + return request({ url: `/admin/coupons/${id}`, method: 'PUT', data }); } export function deleteCoupon(id: number) { - return request.delete(`/admin/coupons/${id}`); + return request({ url: `/admin/coupons/${id}`, method: 'DELETE' }); } diff --git a/apps/server/src/entities/order.entity.ts b/apps/server/src/entities/order.entity.ts index 2570bd3..e4b32d6 100644 --- a/apps/server/src/entities/order.entity.ts +++ b/apps/server/src/entities/order.entity.ts @@ -54,7 +54,7 @@ export class Order { couponDiscount: number; @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: '订单总金额' }) totalAmount: number; diff --git a/apps/server/src/modules/finance/dto/finance.dto.ts b/apps/server/src/modules/finance/dto/finance.dto.ts new file mode 100644 index 0000000..5c09466 --- /dev/null +++ b/apps/server/src/modules/finance/dto/finance.dto.ts @@ -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'; diff --git a/apps/server/src/modules/finance/refund.service.ts b/apps/server/src/modules/finance/refund.service.ts index fd75f6d..fabf133 100644 --- a/apps/server/src/modules/finance/refund.service.ts +++ b/apps/server/src/modules/finance/refund.service.ts @@ -5,11 +5,11 @@ import { ConfigService } from '@nestjs/config'; import { Order } from '@/entities/order.entity'; import { PlatformAccount } from '@/entities/platform-account.entity'; import { PlatformTransaction } from '@/entities/platform-transaction.entity'; -import { Wechatpay } from 'wechatpay-node-v3'; +import Wechatpay = require('wechatpay-node-v3'); @Injectable() export class RefundService { - private wechatpayClient: Wechatpay; + private wechatpayClient: Wechatpay | null; constructor( @InjectRepository(Order) @@ -45,9 +45,10 @@ export class RefundService { this.wechatpayClient = new Wechatpay({ appid, mchid, + publicKey: Buffer.from(privateKey, 'utf-8'), privateKey: Buffer.from(privateKey, 'utf-8'), - serialNo, - apiv3Key, + serial_no: serialNo, + key: apiv3Key, }); console.log('[微信支付] 客户端初始化成功'); } catch (error) { @@ -153,7 +154,7 @@ export class RefundService { const totalAmount = Math.round(order.payAmount * 100); // 调用微信支付退款API - const result = await this.wechatpayClient.refunds({ + const result: any = await this.wechatpayClient.refunds({ transaction_id: order.transactionId, out_refund_no: refundNo, reason: '用户申请退款', diff --git a/apps/server/src/modules/guest/guest.controller.ts b/apps/server/src/modules/guest/guest.controller.ts index 91a68cf..e553b58 100644 --- a/apps/server/src/modules/guest/guest.controller.ts +++ b/apps/server/src/modules/guest/guest.controller.ts @@ -11,7 +11,7 @@ import { } from '@nestjs/common'; import { GuestService } from './guest.service'; 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') @UseGuards(JwtAuthGuard) diff --git a/apps/server/src/modules/merchant/statistics-seller.controller.ts b/apps/server/src/modules/merchant/statistics-seller.controller.ts index e574db6..78b18b9 100644 --- a/apps/server/src/modules/merchant/statistics-seller.controller.ts +++ b/apps/server/src/modules/merchant/statistics-seller.controller.ts @@ -1,9 +1,9 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '@/modules/auth/jwt-auth.guard'; -import { RolesGuard } from '@/modules/auth/roles.guard'; -import { Roles } from '@/modules/auth/roles.decorator'; -import { CurrentUser } from '@/modules/auth/current-user.decorator'; +import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard'; +import { RolesGuard } from '@/common/guards/roles.guard'; +import { Roles } from '@/common/decorators/roles.decorator'; +import { CurrentUser } from '@/common/decorators/current-user.decorator'; import { StatisticsService } from './statistics.service'; @ApiTags('商家统计') diff --git a/apps/server/src/modules/merchant/statistics.service.ts b/apps/server/src/modules/merchant/statistics.service.ts index 4de1dbf..eaabda1 100644 --- a/apps/server/src/modules/merchant/statistics.service.ts +++ b/apps/server/src/modules/merchant/statistics.service.ts @@ -63,7 +63,7 @@ export class StatisticsService { month: monthData, roomCount, balance: account?.balance || 0, - availableBalance: account?.availableBalance || 0, + availableBalance: account ? Number(account.balance) - Number(account.frozenBalance) : 0, }; } diff --git a/apps/server/src/modules/order/order.service.ts b/apps/server/src/modules/order/order.service.ts index 3d3e0c3..34334f6 100644 --- a/apps/server/src/modules/order/order.service.ts +++ b/apps/server/src/modules/order/order.service.ts @@ -82,7 +82,7 @@ export class OrderService { // 处理优惠券 let couponDiscount = 0; - let userCouponId = null; + let userCouponId: number | null = null; if (dto.couponId) { // 查询用户可用优惠券 @@ -138,7 +138,7 @@ export class OrderService { serviceFee, merchantIncome, couponDiscount, - userCouponId, + userCouponId: userCouponId !== null ? userCouponId : undefined, totalAmount, payAmount, contactName: dto.contactName, diff --git a/apps/server/src/schedule/settlement.schedule.ts b/apps/server/src/schedule/settlement.schedule.ts new file mode 100644 index 0000000..81b5d67 --- /dev/null +++ b/apps/server/src/schedule/settlement.schedule.ts @@ -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); + } + } +} diff --git a/database/migrations/001_init_schema.sql b/database/migrations/001_init_schema.sql index 6aeaf49..9a5dadc 100644 --- a/database/migrations/001_init_schema.sql +++ b/database/migrations/001_init_schema.sql @@ -10,8 +10,10 @@ USE `rent_platform`; -- ============================================================ CREATE TABLE `users` ( `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加密存储)', + `wechat_openid` VARCHAR(100) NULL COMMENT '微信openid', + `wechat_unionid` VARCHAR(100) NULL COMMENT '微信unionid', `nickname` VARCHAR(50) DEFAULT '' COMMENT '昵称', `avatar` VARCHAR(500) DEFAULT '' COMMENT '头像URL', `gender` TINYINT UNSIGNED DEFAULT 0 COMMENT '性别 0-未知 1-男 2-女', @@ -22,7 +24,9 @@ CREATE TABLE `users` ( `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 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_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; diff --git a/database/migrations/002_add_wechat_fields.sql b/database/migrations/002_add_wechat_fields.sql deleted file mode 100644 index f6fd812..0000000 --- a/database/migrations/002_add_wechat_fields.sql +++ /dev/null @@ -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. 为这些字段添加索引以提高查询性能