Add 'server/' from commit 'e64421295fff83acbb6d6ab3d3b27f3ef8368f00'

git-subtree-dir: server
git-subtree-mainline: c4e617ada7
git-subtree-split: e64421295f
This commit is contained in:
2026-08-04 15:02:35 +08:00
176 changed files with 13532 additions and 0 deletions
@@ -0,0 +1,26 @@
package controller
import (
"context"
commonHttp "slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
type ad struct{}
var Ad = new(ad)
// RewardClaim 领取广告激励(限频:effect_extra 每日 2 次 / vip_trial 每日 1 次)
func (c *ad) RewardClaim(ctx context.Context, req *dto.AdRewardClaimReq) (res *dto.AdRewardClaimRes, err error) {
result, err := service.AdService.Claim(ctx, commonHttp.GetUserId(g.RequestFromCtx(ctx)), req.AdType)
if err != nil {
return nil, err
}
return &dto.AdRewardClaimRes{Reward: &dto.AdRewardInfo{
AdType: result.AdType, RemainingToday: result.RemainingToday,
}}, nil
}
@@ -0,0 +1,39 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
type avatar struct{}
var Avatar = new(avatar)
func (c *avatar) Build(ctx context.Context, req *dto.AvatarBuildReq) (res *dto.AvatarBuildRes, err error) {
a, err := service.AvatarService.Build(ctx, common.GetUserId(g.RequestFromCtx(ctx)))
if err != nil {
return nil, err
}
return &dto.AvatarBuildRes{AvatarId: a.Id, Status: a.BuildStatus}, nil
}
func (c *avatar) Get(ctx context.Context, req *dto.AvatarGetReq) (res *dto.AvatarGetRes, err error) {
a, err := service.AvatarService.Get(ctx, common.GetUserId(g.RequestFromCtx(ctx)))
if err != nil || a == nil {
return &dto.AvatarGetRes{}, nil
}
return &dto.AvatarGetRes{
FaceTemplateId: a.FaceTemplateId,
BodyTemplateId: a.BodyTemplateId,
SkinToneIndex: a.SkinToneIndex,
GlbUrl: a.GlbUrl,
FramesUrl: a.FramesUrl,
BuildStatus: a.BuildStatus,
Error: a.Error,
}, nil
}
@@ -0,0 +1,49 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/model/entity"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
type body_measurement struct{}
var BodyMeasurement = new(body_measurement)
func (c *body_measurement) Save(ctx context.Context, req *dto.BodyMeasurementSaveReq) (res *struct{}, err error) {
if err := service.BodyMeasurementService.Save(ctx, common.GetUserId(g.RequestFromCtx(ctx)), &entity.BodyMeasurement{
Height: req.Height,
Weight: req.Weight,
SkinTone: req.SkinTone,
Bust: req.Bust,
Waist: req.Waist,
Hip: req.Hip,
Shoulder: req.Shoulder,
FitParams: req.FitParams,
}); err != nil {
return nil, err
}
return &struct{}{}, nil
}
func (c *body_measurement) Get(ctx context.Context, req *dto.BodyMeasurementGetReq) (res *dto.BodyMeasurementGetRes, err error) {
b, err := service.BodyMeasurementService.Get(ctx, common.GetUserId(g.RequestFromCtx(ctx)))
if err != nil || b == nil {
return &dto.BodyMeasurementGetRes{}, nil
}
return &dto.BodyMeasurementGetRes{
Height: b.Height,
Weight: b.Weight,
SkinTone: b.SkinTone,
Bust: b.Bust,
Waist: b.Waist,
Hip: b.Hip,
Shoulder: b.Shoulder,
FitParams: b.FitParams,
}, nil
}
@@ -0,0 +1,17 @@
package controller
import (
"context"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
)
// CategoryList 联盟分类列表(客户端 chips)
func (c *cps) CategoryList(ctx context.Context, req *dto.CpsCategoryListReq) (res *dto.CpsCategoryListRes, err error) {
list, err := service.CpsCategoryService.List(ctx)
if err != nil {
return nil, err
}
return &dto.CpsCategoryListRes{List: list}, nil
}
@@ -0,0 +1,20 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// MyRecent 最近优惠(点击日志 → 商品)
func (c *cps) MyRecent(ctx context.Context, req *dto.CpsMyRecentReq) (res *dto.CpsMyRecentRes, err error) {
list, err := service.CpsClickLogService.MyRecent(ctx, common.GetUserId(g.RequestFromCtx(ctx)))
if err != nil {
return nil, err
}
return &dto.CpsMyRecentRes{List: list}, nil
}
@@ -0,0 +1,35 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// cps 共享 struct/cps/* 簇的 6 个 handler 分文件挂在同一个类型上(组前缀路由零变更)
type cps struct{}
var Cps = new(cps)
// ProductList 选品池分页列表
func (c *cps) ProductList(ctx context.Context, req *dto.CpsProductListReq) (res *dto.CpsProductListRes, err error) {
list, hasMore, err := service.CpsProductService.ListByCategory(ctx, req.Source, req.CategoryCode, req.City, req.Page, 0)
if err != nil {
return nil, err
}
return &dto.CpsProductListRes{List: list, HasMore: hasMore}, nil
}
// ProductLink 商品转链(记录点击日志)
func (c *cps) ProductLink(ctx context.Context, req *dto.CpsProductLinkReq) (res *dto.CpsProductLinkRes, err error) {
r := g.RequestFromCtx(ctx)
link, err := service.CpsProductService.ClickLink(ctx, common.GetUserId(r), req.ProductId, req.Scene, req.PlanId, r.GetClientIp())
if err != nil {
return nil, err
}
return &dto.CpsProductLinkRes{Deeplink: link}, nil
}
@@ -0,0 +1,20 @@
package controller
import (
"context"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
)
type hairstyle struct{}
var Hairstyle = new(hairstyle)
func (c *hairstyle) List(ctx context.Context, req *dto.HairstyleListReq) (res *dto.HairstyleListRes, err error) {
list, err := service.HairstyleService.List(ctx)
if err != nil {
return nil, err
}
return &dto.HairstyleListRes{List: list}, nil
}
@@ -0,0 +1,37 @@
package controller
import (
"context"
commonHttp "slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// member 为 /member/* 簇共享控制器类型(struct 名 kebab-case 决定路由前缀,
// 多表拆分文件但保持同一类型,避免路径漂移)
type member struct{}
var Member = new(member)
// PlanList 会员套餐列表
func (c *member) PlanList(ctx context.Context, req *dto.MemberPlanListReq) (res *dto.MemberPlanListRes, err error) {
list, err := service.MemberPlanService.PlanList(ctx)
if err != nil {
return nil, err
}
return &dto.MemberPlanListRes{List: list}, nil
}
// Status 我的会员状态
func (c *member) Status(ctx context.Context, req *dto.MemberStatusReq) (res *dto.MemberStatusRes, err error) {
st, err := service.MemberPlanService.Status(ctx, commonHttp.GetUserId(g.RequestFromCtx(ctx)))
if err != nil {
return nil, err
}
return &dto.MemberStatusRes{
IsVip: st.IsVip, ExpireAt: st.ExpireAt, PlanName: st.PlanName, Benefits: st.Benefits,
}, nil
}
@@ -0,0 +1,35 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// outfit 为 /outfit/* 簇共享控制器类型(struct 名 kebab-case 决定路由前缀,
// 多表拆分文件但保持同一类型,避免路径漂移)
type outfit struct{}
var Outfit = new(outfit)
// Generate 生成穿搭方案(异步任务)
func (c *outfit) Generate(ctx context.Context, req *dto.OutfitGenerateReq) (res *dto.OutfitGenerateRes, err error) {
taskId, err := service.OutfitService.Generate(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req)
if err != nil {
return nil, err
}
return &dto.OutfitGenerateRes{TaskId: taskId}, nil
}
// TaskStatus 查询生成任务状态
func (c *outfit) TaskStatus(ctx context.Context, req *dto.OutfitTaskStatusReq) (res *dto.OutfitTaskStatusRes, err error) {
status, msg, err := service.OutfitService.GetTaskStatus(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.TaskId)
if err != nil {
return nil, err
}
return &dto.OutfitTaskStatusRes{Status: status, Error: msg}, nil
}
@@ -0,0 +1,33 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// PlanList 方案列表
func (c *outfit) PlanList(ctx context.Context, req *dto.OutfitPlanListReq) (res *dto.OutfitPlanListRes, err error) {
list, err := service.OutfitPlanService.ListPlans(ctx, common.GetUserId(g.RequestFromCtx(ctx)))
if err != nil {
return nil, err
}
return &dto.OutfitPlanListRes{List: list}, nil
}
// PlanDetail 方案详情(items + images + hairstyle
func (c *outfit) PlanDetail(ctx context.Context, req *dto.OutfitPlanDetailReq) (res *dto.OutfitPlanDetailRes, err error) {
return service.OutfitPlanService.GetPlanDetail(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.PlanId)
}
// SelectMain 选定主方案(触发效果图生成)
func (c *outfit) SelectMain(ctx context.Context, req *dto.OutfitSelectMainReq) (res *struct{}, err error) {
if err = service.OutfitPlanService.SelectMain(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.PlanId); err != nil {
return nil, err
}
return &struct{}{}, nil
}
@@ -0,0 +1,21 @@
package controller
import (
"context"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
)
type partner_store struct{}
var PartnerStore = new(partner_store)
// List 合作门店列表
func (c *partner_store) List(ctx context.Context, req *dto.StoreListReq) (res *dto.StoreListRes, err error) {
list, err := service.PartnerStoreService.List(ctx, req.Type)
if err != nil {
return nil, err
}
return &dto.StoreListRes{List: list}, nil
}
@@ -0,0 +1,3 @@
package controller
// pay_notify_log 表控制器:无独立路由 handler(回调日志由 /member/order/notify 审计写入)
@@ -0,0 +1,74 @@
package controller
import (
"context"
"errors"
"fmt"
commonHttp "slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
// OrderCreate 下单 → 返回支付 URL
func (c *member) OrderCreate(ctx context.Context, req *dto.MemberOrderCreateReq) (res *dto.MemberOrderCreateRes, err error) {
order, payURL, err := service.PaymentOrderService.CreateMemberOrder(ctx, commonHttp.GetUserId(g.RequestFromCtx(ctx)), req.PlanId)
if err != nil {
return nil, err
}
return &dto.MemberOrderCreateRes{OrderNo: order.OrderNo, PayUrl: payURL}, nil
}
// OrderStatus 订单状态(App 轮询)
func (c *member) OrderStatus(ctx context.Context, req *dto.MemberOrderStatusReq) (res *dto.MemberOrderStatusRes, err error) {
order, err := service.PaymentOrderService.OrderStatus(ctx, req.OrderNo)
if err != nil || order == nil {
return nil, errors.New("订单不存在")
}
paidAt := ""
if order.PaidAt != nil {
paidAt = order.PaidAt.Format("Y-m-d H:i:s")
}
return &dto.MemberOrderStatusRes{Status: order.Status, TradeNo: order.TradeNo, PaidAt: paidAt}, nil
}
// MemberNotify 虎皮棋支付回调:验签 → 幂等开通 → 返回裸文本 "success"
// 虎皮棋要求回调响应体为字面 "success",故不走统一 JSON 包装(main.go 手动绑定)
func MemberNotify(r *ghttp.Request) {
ctx := r.Context()
body := r.GetBodyString()
hash := r.Get("hash").String()
orderNo := r.Get("trade_order_id").String()
remoteIP := r.GetClientIp()
params := make(map[string]string)
for k, v := range r.GetRequestMap() {
params[k] = fmt.Sprint(v)
}
ok := service.PaymentOrderService.VerifyNotify(params, hash, g.Cfg().MustGet(ctx, "payment.xunhu_appsecret", "").String())
if !ok {
if err := service.PayNotifyLogService.Insert(ctx, orderNo, body, hash, remoteIP, "bad_sign"); err != nil {
g.Log().Warningf(ctx, "写入支付回调日志失败(bad_sign: %v", err)
}
r.Response.Write("fail")
r.ExitAll()
return
}
state, err := service.PaymentOrderService.HandlePaidNotify(ctx, orderNo, r.Get("transaction_id").String(), body)
if logErr := service.PayNotifyLogService.Insert(ctx, orderNo, body, hash, remoteIP, state); logErr != nil {
g.Log().Warningf(ctx, "写入支付回调日志失败: %v", logErr)
}
// duplicate(幂等重复回调)同样返回 success,避免支付渠道无限重试
if err != nil || state == "no_order" {
r.Response.Write("fail")
r.ExitAll()
return
}
r.Response.Write("success")
r.ExitAll()
}
@@ -0,0 +1,4 @@
package controller
// plan_effect_image 表控制器:无独立路由 handler,逻辑归属 outfit/* 簇
// (效果图由选定主方案后异步生成,经 /outfit/plan/detail 返回)
@@ -0,0 +1,4 @@
package controller
// plan_outfit_item 表控制器:无独立路由 handler,逻辑归属 outfit/* 簇
// (方案条目数据由 /outfit/plan/detail 承载)
@@ -0,0 +1,19 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// Review 方案反馈
func (c *outfit) Review(ctx context.Context, req *dto.OutfitReviewReq) (res *struct{}, err error) {
if err = service.PlanReviewService.Review(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.PlanId, req.Action, req.Note); err != nil {
return nil, err
}
return &struct{}{}, nil
}
@@ -0,0 +1,35 @@
package controller
import (
"context"
"errors"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
// PlanRecommend 方案驱动推荐(发型/买同款/到店试穿/场合)
func (c *cps) PlanRecommend(ctx context.Context, req *dto.CpsPlanRecommendReq) (res *dto.CpsPlanRecommendRes, err error) {
userId := common.GetUserId(g.RequestFromCtx(ctx))
plan, err := service.OutfitPlanService.GetPlan(ctx, userId, req.PlanId)
if err != nil || plan == nil {
return nil, errors.New("方案不存在")
}
list, err := service.SceneCategoryMapService.Recommend(ctx, plan, req.Scene)
if err != nil {
return nil, err
}
return &dto.CpsPlanRecommendRes{List: list}, nil
}
// WardrobeUpgrade 衣橱升级款
func (c *cps) WardrobeUpgrade(ctx context.Context, req *dto.CpsWardrobeUpgradeReq) (res *dto.CpsWardrobeUpgradeRes, err error) {
list, err := service.SceneCategoryMapService.WardrobeUpgrade(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.ItemId)
if err != nil {
return nil, err
}
return &dto.CpsWardrobeUpgradeRes{List: list}, nil
}
@@ -0,0 +1,3 @@
package controller
// scoring_rule 表控制器:无独立路由 handler,规则在服务端读取(评分阈值/效果图额度)
@@ -0,0 +1,47 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/dao"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
type user struct{}
var User = new(user)
func (c *user) Register(ctx context.Context, req *dto.RegisterReq) (res *struct{}, err error) {
_, err = service.UserService.Register(ctx, req.Account, req.Password, req.Name)
if err != nil {
return nil, err
}
return &struct{}{}, nil
}
func (c *user) Login(ctx context.Context, req *dto.LoginReq) (res *dto.LoginRes, err error) {
user, token, err := service.UserService.Login(ctx, req.Account, req.Password)
if err != nil {
return nil, err
}
return &dto.LoginRes{
Token: token,
User: &dto.LoginUser{Id: user.Id, Role: user.Role, Name: user.Name},
}, nil
}
func (c *user) ChangePassword(ctx context.Context, req *dto.ChangePasswordReq) (res *struct{}, err error) {
return nil, service.UserService.ChangePassword(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.OldPassword, req.NewPassword)
}
func (c *user) Profile(ctx context.Context, req *dto.ProfileReq) (res *dto.ProfileRes, err error) {
user, err := dao.User.GetOne(ctx, common.GetUserId(g.RequestFromCtx(ctx)))
if err != nil || user == nil {
return nil, err
}
return &dto.ProfileRes{Id: user.Id, Role: user.Role, Name: user.Name, Username: user.Username, Phone: user.Phone}, nil
}
@@ -0,0 +1,4 @@
package controller
// user_member 表控制器:无独立路由 handler(会员状态经 /member/status 返回,
// 开通由支付回调与广告激励写入)
@@ -0,0 +1,38 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
type user_photo struct{}
var UserPhoto = new(user_photo)
func (c *user_photo) Upload(ctx context.Context, req *dto.UserPhotoUploadReq) (res *dto.UserPhotoUploadRes, err error) {
id, err := service.UserPhotoService.Upload(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.Type, g.RequestFromCtx(ctx).GetUploadFile("file"))
if err != nil {
return nil, err
}
return &dto.UserPhotoUploadRes{Id: id}, nil
}
func (c *user_photo) List(ctx context.Context, req *dto.UserPhotoListReq) (res *dto.UserPhotoListRes, err error) {
list, err := service.UserPhotoService.List(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.Type)
if err != nil {
return nil, err
}
return &dto.UserPhotoListRes{List: list}, nil
}
func (c *user_photo) Delete(ctx context.Context, req *dto.UserPhotoDeleteReq) (res *struct{}, err error) {
if err := service.UserPhotoService.Delete(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.Id); err != nil {
return nil, err
}
return &struct{}{}, nil
}
@@ -0,0 +1,61 @@
package controller
import (
"context"
"slogan-agent/common"
"slogan-agent/styleagent/model/dto"
"slogan-agent/styleagent/model/entity"
"slogan-agent/styleagent/service"
"github.com/gogf/gf/v2/frame/g"
)
type wardrobe struct{}
var Wardrobe = new(wardrobe)
func (c *wardrobe) Upload(ctx context.Context, req *dto.WardrobeUploadReq) (res *dto.WardrobeUploadRes, err error) {
id, err := service.WardrobeService.Upload(ctx, common.GetUserId(g.RequestFromCtx(ctx)), entity.WardrobeItem{
Category: req.Category,
Season: req.Season,
StyleTags: req.StyleTags,
ColorInfo: req.ColorInfo,
}, g.RequestFromCtx(ctx).GetUploadFile("file"))
if err != nil {
return nil, err
}
return &dto.WardrobeUploadRes{Id: id}, nil
}
func (c *wardrobe) List(ctx context.Context, req *dto.WardrobeListReq) (res *dto.WardrobeListRes, err error) {
list, err := service.WardrobeService.List(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.Category)
if err != nil {
return nil, err
}
return &dto.WardrobeListRes{List: list}, nil
}
func (c *wardrobe) Update(ctx context.Context, req *dto.WardrobeUpdateReq) (res *struct{}, err error) {
data := map[string]any{}
if req.Category != "" {
data["category"] = req.Category
}
if req.Season != "" {
data["season"] = req.Season
}
if req.StyleTags != "" {
data["style_tags"] = req.StyleTags
}
if err := service.WardrobeService.Update(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.Id, data); err != nil {
return nil, err
}
return &struct{}{}, nil
}
func (c *wardrobe) Delete(ctx context.Context, req *dto.WardrobeDeleteReq) (res *struct{}, err error) {
if err := service.WardrobeService.Delete(ctx, common.GetUserId(g.RequestFromCtx(ctx)), req.Id); err != nil {
return nil, err
}
return &struct{}{}, nil
}