This commit is contained in:
2025-09-10 18:10:40 +08:00
parent 8530e97ab6
commit d50290e8fe
27 changed files with 2025 additions and 3913 deletions

View File

@@ -11,183 +11,16 @@ const dayjs = require('dayjs');
const router = express.Router();
/**
* @swagger
* components:
* schemas:
* Transfer:
* type: object
* properties:
* id:
* type: integer
* description: 转账记录ID
* user_id:
* type: integer
* description: 用户ID
* recipient_id:
* type: integer
* description: 接收方用户ID
* amount:
* type: number
* format: float
* description: 转账金额
* status:
* type: string
* enum: [pending, completed, failed, cancelled]
* description: 转账状态
* transfer_type:
* type: string
* enum: [user_to_user, user_to_system, system_to_user]
* description: 转账类型
* voucher_image:
* type: string
* description: 转账凭证图片路径
* remark:
* type: string
* description: 转账备注
* created_at:
* type: string
* format: date-time
* description: 创建时间
* updated_at:
* type: string
* format: date-time
* description: 更新时间
* Pagination:
* type: object
* properties:
* total:
* type: integer
* description: 总记录数
* page:
* type: integer
* description: 当前页码
* limit:
* type: integer
* description: 每页记录数
* total_pages:
* type: integer
* description: 总页数
*/
// 配置文件上传
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
cb(null, 'voucher-' + uniqueSuffix + path.extname(file.originalname))
}
});
const upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('只允许上传图片文件'));
}
},
limits: {
fileSize: 5 * 1024 * 1024 // 5MB
}
});
/**
* @swagger
* /transfers:
* get:
* summary: 获取转账列表
* tags: [Transfers]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: status
* schema:
* type: string
* description: 转账状态过滤
* - in: query
* name: transfer_type
* schema:
* type: string
* description: 转账类型过滤
* - in: query
* name: start_date
* schema:
* type: string
* format: date
* description: 开始日期过滤
* - in: query
* name: end_date
* schema:
* type: string
* format: date
* description: 结束日期过滤
* - in: query
* name: search
* schema:
* type: string
* description: 搜索关键词(用户名或真实姓名)
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: 页码
* - in: query
* name: limit
* schema:
* type: integer
* default: 10
* description: 每页数量
* - in: query
* name: sort
* schema:
* type: string
* description: 排序字段
* - in: query
* name: order
* schema:
* type: string
* enum: [asc, desc]
* description: 排序方向
* responses:
* 200:
* description: 成功获取转账列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: object
* properties:
* transfers:
* type: array
* items:
* $ref: '#/components/schemas/Transfer'
* pagination:
* $ref: '#/components/schemas/Pagination'
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/',
authenticateToken,
validateQuery(transferSchemas.query),
async (req, res, next) => {
try {
const { page, limit, status, transfer_type, start_date, end_date, search, sort, order } = req.query;
const { page, limit, status, start_date, end_date, search, sort, order } = req.query;
const filters = {
status,
transfer_type,
start_date,
end_date,
search
@@ -216,84 +49,38 @@ router.get('/',
}
);
/**
* @swagger
* /transfers/list:
* get:
* summary: 获取转账记录列表
* tags: [Transfers]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: status
* schema:
* type: string
* description: 转账状态过滤
* - in: query
* name: transfer_type
* schema:
* type: string
* description: 转账类型过滤
* - in: query
* name: start_date
* schema:
* type: string
* format: date
* description: 开始日期过滤
* - in: query
* name: end_date
* schema:
* type: string
* format: date
* description: 结束日期过滤
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: 页码
* - in: query
* name: limit
* schema:
* type: integer
* default: 10
* description: 每页数量
* - in: query
* name: sort
* schema:
* type: string
* description: 排序字段
* - in: query
* name: order
* schema:
* type: string
* enum: [asc, desc]
* description: 排序方向
* responses:
* 200:
* description: 成功获取转账记录列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: object
* properties:
* transfers:
* type: array
* items:
* $ref: '#/components/schemas/Transfer'
* pagination:
* $ref: '#/components/schemas/Pagination'
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/history',authenticateToken,async (req, res, next) => {
try {
const { page, limit, start_date, end_date, search, sort, order } = req.query;
const filters = {
start_date,
end_date,
search
};
// 非管理员只能查看自己相关的转账
if (req.user.role !== 'admin') {
filters.user_id = req.user.id;
}
const result = await transferService.getTransfersHistory(filters, { page, limit, sort, order });
logger.info('Transfer list requested', {
userId: req.user.id,
filters,
resultCount: result.transfers.length
});
res.json({
success: true,
data: result
});
} catch (error) {
next(error);
}
})
router.get('/list',
authenticateToken,
validateQuery(transferSchemas.query),
@@ -331,49 +118,7 @@ router.get('/list',
}
);
/**
* @swagger
* /transfers/public-account:
* get:
* summary: 获取公户信息
* tags: [Transfers]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取公户信息
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* id:
* type: integer
* description: 公户ID
* username:
* type: string
* description: 公户用户名
* example: public_account
* real_name:
* type: string
* description: 公户名称
* balance:
* type: number
* format: float
* description: 公户余额
* 401:
* description: 未授权
* 404:
* description: 公户不存在
* 500:
* description: 服务器错误
*/
router.get('/public-account', authenticateToken, async (req, res) => {
try {
const db = getDB();
@@ -394,66 +139,7 @@ router.get('/public-account', authenticateToken, async (req, res) => {
}
});
/**
* @swagger
* /transfers/create:
* post:
* summary: 创建转账记录
* tags: [Transfers]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - to_user_id
* - amount
* - transfer_type
* properties:
* to_user_id:
* type: integer
* description: 接收方用户ID
* amount:
* type: number
* format: float
* description: 转账金额
* transfer_type:
* type: string
* enum: [user_to_user, user_to_system, system_to_user]
* description: 转账类型
* remark:
* type: string
* description: 转账备注
* responses:
* 201:
* description: 转账记录创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 转账记录创建成功,等待确认
* data:
* type: object
* properties:
* transfer_id:
* type: integer
* description: 转账记录ID
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.post('/create',
authenticateToken,
validate(transferSchemas.create),
@@ -478,72 +164,7 @@ router.post('/create',
}
);
/**
* @swagger
* /transfers/admin/create:
* post:
* summary: 管理员创建转账记录
* tags: [Transfers]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - from_user_id
* - to_user_id
* - amount
* - transfer_type
* properties:
* from_user_id:
* type: integer
* description: 发送方用户ID
* to_user_id:
* type: integer
* description: 接收方用户ID
* amount:
* type: number
* format: float
* description: 转账金额
* transfer_type:
* type: string
* enum: [user_to_user, user_to_system, system_to_user]
* description: 转账类型
* description:
* type: string
* description: 转账描述
* responses:
* 201:
* description: 转账记录创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 转账记录创建成功
* data:
* type: object
* properties:
* transfer_id:
* type: integer
* description: 转账记录ID
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 403:
* description: 权限不足
* 500:
* description: 服务器错误
*/
router.post('/admin/create',
authenticateToken,
async (req, res, next) => {