527 lines
14 KiB
Go
527 lines
14 KiB
Go
package developer
|
|
|
|
import (
|
|
"fmt"
|
|
"verification-platform-backend/internal/database"
|
|
"verification-platform-backend/internal/model"
|
|
"verification-platform-backend/internal/service"
|
|
"verification-platform-backend/pkg/response"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type FinanceRecordResponse struct {
|
|
ID uint `json:"id"`
|
|
OrderNo string `json:"order_no"`
|
|
Type string `json:"type"`
|
|
UserID uint `json:"user_id"`
|
|
AppID uint `json:"app_id"`
|
|
Amount float64 `json:"amount"`
|
|
Detail string `json:"detail"`
|
|
Status string `json:"status"`
|
|
PaymentType string `json:"payment_type"`
|
|
Remark string `json:"remark"`
|
|
CreatedAt string `json:"created_at"`
|
|
User *struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
} `json:"user,omitempty"`
|
|
}
|
|
|
|
func SetupFinanceRoutes(r *gin.RouterGroup) {
|
|
finance := r.Group("/finance")
|
|
{
|
|
finance.GET("/stats", handleGetFinanceStatistics)
|
|
finance.GET("/statistics", handleGetFinanceStatistics)
|
|
finance.GET("/recharge-records", handleGetRechargeRecords)
|
|
finance.GET("/consumption-records", handleGetConsumptionRecords)
|
|
finance.GET("/records", handleGetFinanceRecords)
|
|
finance.DELETE("/records/:id", handleDeleteFinanceRecord)
|
|
finance.POST("/records/batch-delete", handleBatchDeleteFinanceRecords)
|
|
}
|
|
}
|
|
|
|
func handleGetFinanceStatistics(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
var appIDs []uint
|
|
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
|
|
|
|
if len(appIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"total_income": 0,
|
|
"total_expense": 0,
|
|
"net_profit": 0,
|
|
"monthly_transactions": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var appUserIDs []uint
|
|
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
|
|
|
|
if len(appUserIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"total_income": 0,
|
|
"total_expense": 0,
|
|
"net_profit": 0,
|
|
"monthly_transactions": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var totalIncome float64
|
|
var totalExpense float64
|
|
|
|
database.DB.Model(&model.RechargeRecord{}).
|
|
Where("user_id IN ? AND status = ?", appUserIDs, "success").
|
|
Select("COALESCE(SUM(amount), 0)").
|
|
Scan(&totalIncome)
|
|
|
|
database.DB.Model(&model.ConsumptionRecord{}).
|
|
Where("user_id IN ? AND status = ?", appUserIDs, "success").
|
|
Select("COALESCE(SUM(amount), 0)").
|
|
Scan(&totalExpense)
|
|
|
|
response.Success(c, gin.H{
|
|
"total_income": totalIncome,
|
|
"total_expense": totalExpense,
|
|
"net_profit": totalIncome - totalExpense,
|
|
})
|
|
}
|
|
|
|
func handleGetRechargeRecords(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
var appIDs []uint
|
|
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
|
|
|
|
if len(appIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"records": []model.RechargeRecord{},
|
|
"total": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var appUserIDs []uint
|
|
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
|
|
|
|
if len(appUserIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"records": []model.RechargeRecord{},
|
|
"total": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var records []model.RechargeRecord
|
|
query := database.DB.Model(&model.RechargeRecord{}).Preload("AppUser").Where("user_id IN ?", appUserIDs)
|
|
|
|
search := c.Query("search")
|
|
status := c.Query("status")
|
|
startDate := c.Query("start_date")
|
|
endDate := c.Query("end_date")
|
|
|
|
if search != "" {
|
|
query = query.Where("order_no LIKE ? OR card_code LIKE ?", "%"+search+"%", "%"+search+"%")
|
|
}
|
|
if status != "" && status != "all" {
|
|
query = query.Where("status = ?", status)
|
|
}
|
|
if startDate != "" {
|
|
query = query.Where("created_at >= ?", startDate)
|
|
}
|
|
if endDate != "" {
|
|
query = query.Where("created_at <= ?", endDate+" 23:59:59")
|
|
}
|
|
|
|
if err := query.Order("created_at DESC").Find(&records).Error; err != nil {
|
|
response.Error(c, 500, "获取充值记录失败")
|
|
return
|
|
}
|
|
response.Success(c, gin.H{
|
|
"records": records,
|
|
"total": len(records),
|
|
})
|
|
}
|
|
|
|
func handleGetConsumptionRecords(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
var appIDs []uint
|
|
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
|
|
|
|
if len(appIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"records": []model.ConsumptionRecord{},
|
|
"total": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var appUserIDs []uint
|
|
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
|
|
|
|
if len(appUserIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"records": []model.ConsumptionRecord{},
|
|
"total": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var records []model.ConsumptionRecord
|
|
query := database.DB.Model(&model.ConsumptionRecord{}).Preload("AppUser").Where("user_id IN ?", appUserIDs)
|
|
|
|
search := c.Query("search")
|
|
recordType := c.Query("type")
|
|
status := c.Query("status")
|
|
startDate := c.Query("start_date")
|
|
endDate := c.Query("end_date")
|
|
|
|
if search != "" {
|
|
query = query.Where("order_no LIKE ? OR content LIKE ?", "%"+search+"%", "%"+search+"%")
|
|
}
|
|
if recordType != "" && recordType != "all" {
|
|
query = query.Where("type = ?", recordType)
|
|
}
|
|
if status != "" && status != "all" {
|
|
query = query.Where("status = ?", status)
|
|
}
|
|
if startDate != "" {
|
|
query = query.Where("created_at >= ?", startDate)
|
|
}
|
|
if endDate != "" {
|
|
query = query.Where("created_at <= ?", endDate+" 23:59:59")
|
|
}
|
|
|
|
if err := query.Order("created_at DESC").Find(&records).Error; err != nil {
|
|
response.Error(c, 500, "获取消费记录失败")
|
|
return
|
|
}
|
|
response.Success(c, gin.H{
|
|
"records": records,
|
|
"total": len(records),
|
|
})
|
|
}
|
|
|
|
func handleGetFinanceRecords(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
var appIDs []uint
|
|
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
|
|
|
|
if len(appIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"recharge_records": []FinanceRecordResponse{},
|
|
"consumption_records": []FinanceRecordResponse{},
|
|
"total": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
var appUserIDs []uint
|
|
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
|
|
|
|
if len(appUserIDs) == 0 {
|
|
response.Success(c, gin.H{
|
|
"recharge_records": []FinanceRecordResponse{},
|
|
"consumption_records": []FinanceRecordResponse{},
|
|
"total": 0,
|
|
})
|
|
return
|
|
}
|
|
|
|
page := c.DefaultQuery("page", "1")
|
|
pageSize := c.DefaultQuery("page_size", "10")
|
|
search := c.Query("search")
|
|
recordType := c.Query("type")
|
|
status := c.Query("status")
|
|
startDate := c.Query("start_date")
|
|
endDate := c.Query("end_date")
|
|
|
|
var rechargeRecords []model.RechargeRecord
|
|
rechargeQuery := database.DB.Model(&model.RechargeRecord{}).Preload("AppUser").Where("user_id IN ?", appUserIDs)
|
|
|
|
if search != "" {
|
|
rechargeQuery = rechargeQuery.Where("order_no LIKE ? OR card_code LIKE ?", "%"+search+"%", "%"+search+"%")
|
|
}
|
|
if recordType == "recharge" || recordType == "" || recordType == "all" {
|
|
if status != "" && status != "all" {
|
|
rechargeQuery = rechargeQuery.Where("status = ?", status)
|
|
}
|
|
if startDate != "" {
|
|
rechargeQuery = rechargeQuery.Where("created_at >= ?", startDate)
|
|
}
|
|
if endDate != "" {
|
|
rechargeQuery = rechargeQuery.Where("created_at <= ?", endDate+" 23:59:59")
|
|
}
|
|
if err := rechargeQuery.Order("created_at DESC").Find(&rechargeRecords).Error; err != nil {
|
|
response.Error(c, 500, "获取充值记录失败")
|
|
return
|
|
}
|
|
}
|
|
|
|
var consumptionRecords []model.ConsumptionRecord
|
|
consumptionQuery := database.DB.Model(&model.ConsumptionRecord{}).Preload("AppUser").Where("user_id IN ?", appUserIDs)
|
|
|
|
if search != "" {
|
|
consumptionQuery = consumptionQuery.Where("order_no LIKE ? OR content LIKE ?", "%"+search+"%", "%"+search+"%")
|
|
}
|
|
if recordType == "consumption" || recordType == "" || recordType == "all" {
|
|
if status != "" && status != "all" {
|
|
consumptionQuery = consumptionQuery.Where("status = ?", status)
|
|
}
|
|
if startDate != "" {
|
|
consumptionQuery = consumptionQuery.Where("created_at >= ?", startDate)
|
|
}
|
|
if endDate != "" {
|
|
consumptionQuery = consumptionQuery.Where("created_at <= ?", endDate+" 23:59:59")
|
|
}
|
|
if err := consumptionQuery.Order("created_at DESC").Find(&consumptionRecords).Error; err != nil {
|
|
response.Error(c, 500, "获取消费记录失败")
|
|
return
|
|
}
|
|
}
|
|
|
|
var allRecords []FinanceRecordResponse
|
|
for _, r := range rechargeRecords {
|
|
var user *struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
}
|
|
if r.AppUser != nil && r.AppUser.ID != 0 {
|
|
user = &struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
}{
|
|
ID: r.AppUser.ID,
|
|
Username: r.AppUser.Username,
|
|
Email: r.AppUser.Email,
|
|
}
|
|
}
|
|
allRecords = append(allRecords, FinanceRecordResponse{
|
|
ID: r.ID,
|
|
OrderNo: r.OrderNo,
|
|
Type: "recharge",
|
|
UserID: r.UserID,
|
|
AppID: r.AppUser.ApplicationID,
|
|
Amount: r.Amount,
|
|
Detail: r.CardCode,
|
|
Status: r.Status,
|
|
PaymentType: r.PaymentType,
|
|
Remark: r.Remark,
|
|
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
User: user,
|
|
})
|
|
}
|
|
|
|
for _, r := range consumptionRecords {
|
|
var user *struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
}
|
|
if r.AppUser != nil && r.AppUser.ID != 0 {
|
|
user = &struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
}{
|
|
ID: r.AppUser.ID,
|
|
Username: r.AppUser.Username,
|
|
Email: r.AppUser.Email,
|
|
}
|
|
}
|
|
allRecords = append(allRecords, FinanceRecordResponse{
|
|
ID: r.ID,
|
|
OrderNo: r.OrderNo,
|
|
Type: "consumption",
|
|
UserID: r.UserID,
|
|
AppID: r.AppUser.ApplicationID,
|
|
Amount: r.Amount,
|
|
Detail: r.Content,
|
|
Status: r.Status,
|
|
PaymentType: r.PaymentType,
|
|
Remark: r.Remark,
|
|
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
User: user,
|
|
})
|
|
}
|
|
|
|
total := len(allRecords)
|
|
start := 0
|
|
end := total
|
|
if p, err := parseInt(page); err == nil && p > 0 {
|
|
if ps, err := parseInt(pageSize); err == nil && ps > 0 {
|
|
start = (p - 1) * ps
|
|
end = start + ps
|
|
if start > total {
|
|
start = total
|
|
}
|
|
if end > total {
|
|
end = total
|
|
}
|
|
}
|
|
}
|
|
|
|
if start > end {
|
|
start = end
|
|
}
|
|
|
|
response.Success(c, gin.H{
|
|
"records": allRecords[start:end],
|
|
"total": total,
|
|
})
|
|
}
|
|
|
|
func parseInt(s string) (int, error) {
|
|
var result int
|
|
_, err := fmt.Sscanf(s, "%d", &result)
|
|
return result, err
|
|
}
|
|
|
|
func handleDeleteFinanceRecord(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
recordID := c.Param("id")
|
|
recordType := c.Query("type")
|
|
|
|
var appIDs []uint
|
|
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
|
|
if len(appIDs) == 0 {
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
|
|
var appUserIDs []uint
|
|
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
|
|
if len(appUserIDs) == 0 {
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
|
|
var deletedType string
|
|
var deletedAmount float64
|
|
|
|
if recordType == "recharge" {
|
|
var record model.RechargeRecord
|
|
if err := database.DB.Where("id = ? AND user_id IN ?", recordID, appUserIDs).First(&record).Error; err != nil {
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
if err := database.DB.Delete(&record).Error; err != nil {
|
|
response.Error(c, 500, "删除失败")
|
|
return
|
|
}
|
|
deletedType = "充值记录"
|
|
deletedAmount = record.Amount
|
|
} else if recordType == "consumption" {
|
|
var record model.ConsumptionRecord
|
|
if err := database.DB.Where("id = ? AND user_id IN ?", recordID, appUserIDs).First(&record).Error; err != nil {
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
if err := database.DB.Delete(&record).Error; err != nil {
|
|
response.Error(c, 500, "删除失败")
|
|
return
|
|
}
|
|
deletedType = "消费记录"
|
|
deletedAmount = record.Amount
|
|
} else {
|
|
var rechargeRecord model.RechargeRecord
|
|
var consumptionRecord model.ConsumptionRecord
|
|
|
|
if err := database.DB.Where("id = ? AND user_id IN ?", recordID, appUserIDs).First(&rechargeRecord).Error; err == nil {
|
|
database.DB.Delete(&rechargeRecord)
|
|
deletedType = "充值记录"
|
|
deletedAmount = rechargeRecord.Amount
|
|
service.LogOperation(c, "delete", "finance_record", nil, fmt.Sprintf("删除%s: %.2f", deletedType, deletedAmount), nil)
|
|
response.Success(c, nil)
|
|
return
|
|
}
|
|
|
|
if err := database.DB.Where("id = ? AND user_id IN ?", recordID, appUserIDs).First(&consumptionRecord).Error; err == nil {
|
|
database.DB.Delete(&consumptionRecord)
|
|
deletedType = "消费记录"
|
|
deletedAmount = consumptionRecord.Amount
|
|
service.LogOperation(c, "delete", "finance_record", nil, fmt.Sprintf("删除%s: %.2f", deletedType, deletedAmount), nil)
|
|
response.Success(c, nil)
|
|
return
|
|
}
|
|
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
|
|
service.LogOperation(c, "delete", "finance_record", nil, fmt.Sprintf("删除%s: %.2f", deletedType, deletedAmount), nil)
|
|
|
|
response.Success(c, nil)
|
|
}
|
|
|
|
func handleBatchDeleteFinanceRecords(c *gin.Context) {
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
IDs []uint `json:"ids"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
if len(req.IDs) == 0 {
|
|
response.Error(c, 400, "请选择要删除的记录")
|
|
return
|
|
}
|
|
|
|
var appIDs []uint
|
|
database.DB.Model(&model.Application{}).Where("user_id = ?", userID).Pluck("id", &appIDs)
|
|
if len(appIDs) == 0 {
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
|
|
var appUserIDs []uint
|
|
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Pluck("id", &appUserIDs)
|
|
if len(appUserIDs) == 0 {
|
|
response.Error(c, 404, "记录不存在")
|
|
return
|
|
}
|
|
|
|
database.DB.Where("id IN ? AND user_id IN ?", req.IDs, appUserIDs).Delete(&model.RechargeRecord{})
|
|
database.DB.Where("id IN ? AND user_id IN ?", req.IDs, appUserIDs).Delete(&model.ConsumptionRecord{})
|
|
|
|
service.LogOperation(c, "batch_delete", "finance_record", nil, fmt.Sprintf("批量删除财务记录: %d条", len(req.IDs)), nil)
|
|
|
|
response.Success(c, gin.H{"deleted": len(req.IDs)})
|
|
}
|