30a8e791a3
- public_product_screen: 1:1 复刻设计稿,新增商品介绍、批次防伪(自定义圆形印章)、完整商品参数表、门店营业时间、页脚链接 - app_theme: 完整品牌色系(brand/gray/accent/semantic) - shop model: 新增 business_hours 字段,public 接口同步返回 - nginx: 新增 /product/ → Flutter app 扫码入口,修复 /docs /download html 后缀兼容 - web/: 新增宣传主页、功能页、文档页、扫码 HTML 及设计资源 - app_shell: 界面优化 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
103 lines
2.4 KiB
Go
103 lines
2.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/wangjia/jiu/backend/internal/model"
|
|
)
|
|
|
|
type PublicHandler struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewPublicHandler(db *gorm.DB) *PublicHandler {
|
|
return &PublicHandler{db: db}
|
|
}
|
|
|
|
// GetProduct GET /api/v1/public/products/:public_id (no auth)
|
|
func (h *PublicHandler) GetProduct(c *gin.Context) {
|
|
publicID := c.Param("public_id")
|
|
|
|
var product model.Product
|
|
if err := h.db.Where("public_id = ? AND deleted_at IS NULL", publicID).
|
|
Preload("Images").
|
|
First(&product).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
|
return
|
|
}
|
|
|
|
// Fetch shop public info
|
|
var shop model.Shop
|
|
shopData := gin.H{}
|
|
if err := h.db.Where("id = ?", product.ShopID).First(&shop).Error; err == nil {
|
|
shopData = gin.H{
|
|
"name": shop.Name,
|
|
"code": shop.Code,
|
|
"address": shop.Address,
|
|
"phone": shop.Phone,
|
|
"business_hours": shop.BusinessHours,
|
|
}
|
|
}
|
|
|
|
// Fetch latest inventory batch for this product
|
|
var inv model.Inventory
|
|
batchData := gin.H(nil)
|
|
if err := h.db.Where("product_id = ? AND quantity > 0 AND deleted_at IS NULL", product.ID).
|
|
Order("created_at DESC").
|
|
First(&inv).Error; err == nil {
|
|
var pdStr *string
|
|
if inv.ProductionDate != nil {
|
|
s := inv.ProductionDate.Time.Format("2006-01-02")
|
|
pdStr = &s
|
|
}
|
|
batchData = gin.H{
|
|
"production_date": pdStr,
|
|
"batch_no": inv.BatchNo,
|
|
"in_stock_date": inv.CreatedAt.Format("2006-01-02"),
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"data": gin.H{
|
|
"id": product.ID,
|
|
"name": product.Name,
|
|
"series": product.Series,
|
|
"spec": product.Spec,
|
|
"brand": product.Brand,
|
|
"unit": product.Unit,
|
|
"description": product.Description,
|
|
"images": product.Images,
|
|
"shop": shopData,
|
|
"batch": batchData,
|
|
},
|
|
})
|
|
}
|
|
|
|
// GetRelease GET /api/v1/public/release (no auth)
|
|
func (h *PublicHandler) GetRelease(c *gin.Context) {
|
|
version := os.Getenv("APP_VERSION")
|
|
if version == "" {
|
|
version = "1.0.0"
|
|
}
|
|
buildDate := os.Getenv("BUILD_DATE")
|
|
if buildDate == "" {
|
|
buildDate = time.Now().Format("2006-01-02")
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"version": version,
|
|
"build_date": buildDate,
|
|
"download_urls": gin.H{
|
|
"macos": os.Getenv("DOWNLOAD_URL_MACOS"),
|
|
"windows": os.Getenv("DOWNLOAD_URL_WINDOWS"),
|
|
"web": "https://jiu.51yanmei.com/app",
|
|
},
|
|
"changelog": []gin.H{},
|
|
})
|
|
}
|