140 lines
3.8 KiB
Go
140 lines
3.8 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
_ "oci-portal/internal/oci" // swagger 注解引用
|
|
)
|
|
|
|
// payInvoiceRequest 是付款请求体;email 接收 OSP 付款回执。
|
|
type payInvoiceRequest struct {
|
|
Email string `json:"email" binding:"required"`
|
|
}
|
|
|
|
// @Summary 发票列表
|
|
// @Tags 账单
|
|
// @Param id path int true "配置 ID"
|
|
// @Param year query int false "自然年过滤(按开票时间);缺省全量"
|
|
// @Success 200 {array} oci.Invoice
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/invoices [get]
|
|
func (h *ociConfigHandler) invoices(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
year, _ := strconv.Atoi(c.Query("year"))
|
|
list, err := h.svc.Invoices(c.Request.Context(), id, year)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, list)
|
|
}
|
|
|
|
// @Summary 发票费用明细
|
|
// @Tags 账单
|
|
// @Param id path int true "配置 ID"
|
|
// @Param internalId path string true "发票内部 ID"
|
|
// @Success 200 {array} oci.InvoiceLine
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/invoices/{internalId}/lines [get]
|
|
func (h *ociConfigHandler) invoiceLines(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
lines, err := h.svc.InvoiceLines(c.Request.Context(), id, c.Param("internalId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, lines)
|
|
}
|
|
|
|
// @Summary 发票 PDF 下载
|
|
// @Tags 账单
|
|
// @Param id path int true "配置 ID"
|
|
// @Param internalId path string true "发票内部 ID"
|
|
// @Param number query string false "发票号(用作下载文件名)"
|
|
// @Success 200 {file} binary "PDF 原文"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/invoices/{internalId}/pdf [get]
|
|
func (h *ociConfigHandler) invoicePdf(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
data, err := h.svc.InvoicePdf(c.Request.Context(), id, c.Param("internalId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", pdfFileName(c.Query("number"), c.Param("internalId"))))
|
|
c.Data(http.StatusOK, "application/pdf", data)
|
|
}
|
|
|
|
// pdfFileName 生成下载文件名:优先发票号,剔除引号/路径分隔等不安全字符。
|
|
func pdfFileName(number, internalID string) string {
|
|
name := strings.TrimSpace(number)
|
|
if name == "" {
|
|
name = internalID
|
|
}
|
|
name = strings.Map(func(r rune) rune {
|
|
if r == '"' || r == '/' || r == '\\' || r < 0x20 {
|
|
return '_'
|
|
}
|
|
return r
|
|
}, name)
|
|
return name + ".pdf"
|
|
}
|
|
|
|
// @Summary 支付发票
|
|
// @Tags 账单
|
|
// @Param id path int true "配置 ID"
|
|
// @Param internalId path string true "发票内部 ID"
|
|
// @Param body body payInvoiceRequest true "回执邮箱"
|
|
// @Success 200 {object} map[string]string
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/invoices/{internalId}/pay [post]
|
|
func (h *ociConfigHandler) payInvoice(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req payInvoiceRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if err := h.svc.PayInvoice(c.Request.Context(), id, c.Param("internalId"), req.Email); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"status": "submitted"})
|
|
}
|
|
|
|
// @Summary 付款方式列表
|
|
// @Tags 账单
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.PaymentMethod
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/payment-methods [get]
|
|
func (h *ociConfigHandler) paymentMethods(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
methods, err := h.svc.PaymentMethods(c.Request.Context(), id)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, methods)
|
|
}
|