You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

176 lines
6.1 KiB

package pay
import (
"fmt"
"time"
"yunyan/comm"
"yunyan/lego/base"
"yunyan/lego/core"
"yunyan/lego/sys/log"
"yunyan/modules"
"yunyan/pb"
)
type apiComp struct {
modules.MCompHttpGate
service base.IRPCXService
module *Pay
options *Options
}
func (this *apiComp) Init(service core.IService, module core.IModule, comp core.IModuleComp, options core.IModuleOptions) (err error) {
this.MCompHttpGate.Init(service, module, comp, options)
this.service = service.(base.IRPCXService)
this.module = module.(*Pay)
this.options = options.(*Options)
return
}
func (this *apiComp) Start() (err error) {
if err = this.MCompHttpGate.Start(); err != nil {
return
}
if m, e := this.service.GetModule(comm.ModuleAnalyze); e == nil {
this.module.analyze, _ = m.(comm.IAnalyze)
}
return
}
// goodsGrant 一笔订单要发放的内容。商品来源有两套——历史口径读**各业务库的 goods 表**
// (pb.DBGoods),v2 口径读**管理后台的商品配置**(console 公共库 app_goods,按应用名隔离)——
// 但发放、记账、埋点是同一套,抽出来共用。
type goodsGrant struct {
Viptime int32
Ainum int64
Meetnum int64
Tradenum int64
}
// grantOfAppGoods 把后台商品配置换算成发放内容(v2 口径)。
func grantOfAppGoods(g *comm.AppGoods) *goodsGrant {
return &goodsGrant{Viptime: g.Viptime, Ainum: g.Ainum, Meetnum: g.Meetnum, Tradenum: g.Tradenum}
}
// deliverResourceByOrder 按订单给用户发货:商品取自**业务库 goods 表**(历史口径,
// 微信/支付宝/Apple/Google 通知与 verifyorder 都走这条)。
// 商品对不上时 grant 为 nil:不加资源,但仍走一遍更新与记账,与改造前行为一致。
func (this *apiComp) deliverResourceByOrder(user *pb.DBUser, order *pb.DBPayOrder) (err error) {
var goodss []*pb.DBGoods
// 1. 检查订单状态
if order.Status != pb.PayOrderStatus_PAY_ORDER_PAID {
err = fmt.Errorf("订单状态错误,无法下发资源: %v", order.Status)
return
}
goodss, err = this.module.model.goodss()
if err != nil {
err = fmt.Errorf("查询商品信息失败: %v", err)
return
}
var grant *goodsGrant
for _, v := range goodss {
if v.Id == order.ProductId {
grant = &goodsGrant{Viptime: v.Viptime, Ainum: v.Ainum, Meetnum: v.Meetnum, Tradenum: v.Tradenum}
}
}
return this.deliverGrant(user, order, grant)
}
// deliverGrant 真正的发货:改用户资源 → 写消费日志 → 上报埋点。
// grant 为 nil 表示没匹配到商品(不加任何资源,也不写发放明细)。
func (this *apiComp) deliverGrant(user *pb.DBUser, order *pb.DBPayOrder, grant *goodsGrant) (err error) {
var userlog *pb.DBUserUseLog
if order.Status != pb.PayOrderStatus_PAY_ORDER_PAID {
err = fmt.Errorf("订单状态错误,无法下发资源: %v", order.Status)
return
}
now := time.Now()
userlog = &pb.DBUserUseLog{
Uid: order.Uid,
Logtype: pb.UserLogType_Pay,
Ts: now.Unix(),
}
if v := grant; v != nil {
if v.Viptime > 0 {
user.Viplv = 1
prev := user.Vipexptime
days := int64(v.Viptime) * 24 * 60 * 60
mode := "reset"
if user.Vipexptime == 0 || user.Vipexptime < now.Unix() {
user.Vipexptime = now.Unix() + days
} else {
user.Vipexptime = user.Vipexptime + days
mode = "accumulate"
}
userlog.Addvipday = int64(v.Viptime)
this.module.Debug("支付发货",
log.Field{Key: "now", Value: now.Format("2006-01-02 15:04:05")},
log.Field{Key: "prev", Value: prev},
log.Field{Key: "days", Value: days},
log.Field{Key: "mode", Value: mode},
log.Field{Key: "Vipexptime", Value: user.Vipexptime})
}
if v.Ainum > 0 {
user.Aichatintegral += v.Ainum
user.Aichattotalintegral = user.Aichatintegral
userlog.Addagentintegral = v.Ainum
this.module.Debug("支付发货", log.Field{Key: "Aichatintegral", Value: user.Aichatintegral})
}
if v.Meetnum > 0 {
user.Meetintegral += v.Meetnum
user.Meettotalintegral = user.Meetintegral
userlog.Addmeetsecond = int64(v.Meetnum)
this.module.Debug("支付发货", log.Field{Key: "Meetintegral", Value: user.Meetintegral})
}
if v.Tradenum > 0 {
user.Tradeintegral += v.Tradenum
user.Tradetotalintegral = user.Tradeintegral
userlog.Addtradesecond = int64(v.Tradenum)
this.module.Debug("支付发货", log.Field{Key: "Tradeintegral", Value: user.Tradeintegral})
}
userlog.Extra = fmt.Sprintf(`{"order_id":"%s","product_id":"%s","product_name":"%s","amount":%d,"payment_method":%d,"add_vip_days":%d,"add_ai_integral":%d,"add_trade_second":%d,"add_meet_second":%d}`,
order.OrderId, order.ProductId, order.ProductName, order.Amount, order.PaymentMethod,
userlog.Addvipday, userlog.Addagentintegral, userlog.Addtradesecond, userlog.Addmeetsecond)
}
// 2. 更新用户信息
if err = this.module.model.updateUser(user); err != nil {
err = fmt.Errorf("更新用户信息失败: %v", err)
return
}
// 3. 写消费日志
if err = this.module.model.addIntegralLog(userlog); err != nil {
err = fmt.Errorf("添加积分日志失败: %v", err)
return
}
// 4. 埋点:订单支付完成 + 资源发放
if this.module.analyze != nil {
// 归属用订单自己的来源设备快照(下单时冻结),与月结算口径一致;
// 老订单没有快照时回退用户最后绑定信息。
paidPid, paidChannel := order.SrcProductid, order.SrcChannelid
if paidPid == 0 {
paidPid = user.Lastbindproductid
}
if paidChannel == "" {
paidChannel = user.Lastbindchannelid
}
this.module.analyze.Report(&comm.StatEvent{
Type: comm.StatEventOrderPaid,
ProductId: paidPid,
ChannelId: paidChannel,
Uid: order.Uid,
Amount: order.Amount,
})
if userlog.Addvipday > 0 || userlog.Addagentintegral > 0 || userlog.Addtradesecond > 0 || userlog.Addmeetsecond > 0 {
this.module.analyze.Report(&comm.StatEvent{
Type: comm.StatEventResourceGrant,
ProductId: paidPid,
ChannelId: paidChannel,
Uid: order.Uid,
GrantVipDay: userlog.Addvipday,
GrantAiIntegral: userlog.Addagentintegral,
GrantTradeSecond: userlog.Addtradesecond,
GrantMeetSecond: userlog.Addmeetsecond,
})
}
}
return
}