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.
 
 
 
 
 
 

161 lines
6.3 KiB

package console
import (
"strings"
"time"
"yunyan/comm"
"yunyan/lego/sys/postgres"
"yunyan/pb"
"github.com/gin-gonic/gin"
)
// ============================ 渠道分发 (channelapp) ============================
//
// 「SaaS 管理 → 渠道分发」的后端:管理各应用在各分发渠道(华为/小米/OPPO/VIVO/荣耀/官方/苹果/谷歌)
// 上的版本与开关。模型见 comm/channelapp.go,存 console 主库(postgres.GetSys()),按 app_name 作用域,
// 业务服务(home 的 user 模块)运行时按 (app_name=? OR app_name='') 直读下发。
//
// 旧实现(deep_server modules/api/api_*channelapp.go + 各应用业务库 mysql 的 channelapp 表)已下线:
// 数据统一收到 console 主库,后台一个库即可总览全部应用的渠道版本。
//
// 支付:本页只管「这个渠道开放哪几种支付」(PayChannels);各支付方式的商户凭据仍在
// 「应用中心 → 业务功能配置」的支付分组(app_module_config)里配,不在此重复。
// getChannelPayStatus 负责把两边对上——告诉前端哪些已开的支付渠道其实还没配凭据。
// getChannelApps 列出某作用域下的渠道配置(app_name=''为全局默认)。
func (this *serverComp) getChannelApps(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
}
_ = c.ShouldBindJSON(&req)
items := make([]*comm.ChannelApp, 0)
if err := postgres.Find(comm.TableChannelAppCfg, &items, "app_name=?", req.AppName); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
writeOK(c, gin.H{"items": items})
}
// saveChannelApp 新增或更新一个渠道配置(按 app_name+channel upsert)。
func (this *serverComp) saveChannelApp(c *gin.Context) {
var m comm.ChannelApp
if err := c.ShouldBindJSON(&m); err != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
return
}
if m.Channel == 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "渠道 必选")
return
}
// 强更必须有最低支持版本,否则「开了强更却挡不住任何人」,是配置期就能发现的错
if m.ForceUpdate && strings.TrimSpace(m.MinVersion) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "开启强制更新时必须填「最低支持版本」")
return
}
// 最低支持版本不该高于当前线上版本,否则线上用户全被判定为需强更却又没有更高版本可更
if strings.TrimSpace(m.MinVersion) != "" && strings.TrimSpace(m.Version) != "" &&
comm.CompareVersion(m.MinVersion, m.Version) > 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "「最低支持版本」不能高于「当前线上版本」")
return
}
// 游客登录是白名单语义:只有被点名的那一版可见。勾了开关却不填版本 = 谁都看不到,
// 等于没开——这是配置期就能发现的错,别让运营以为"已经开了"再去查客户端。
// 要全部隐藏就直接取消勾选,不要靠留空版本号来表达。
if m.Tourists && strings.TrimSpace(m.TouristsVersion) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "开启游客登录时必须填「生效版本」——留空等于所有版本都看不到入口;要全部隐藏请直接取消勾选")
return
}
if strings.TrimSpace(m.TouristsVersion) != "" && !m.Tourists {
writeErr(c, pb.ErrorCode_ReqParameterError, "填了「游客登录生效版本」就必须同时勾选「允许游客登录」,否则该版本仍然看不到入口")
return
}
now := time.Now().UnixMilli()
m.Updatetime = now
old := &comm.ChannelApp{}
err := postgres.FindOne(comm.TableChannelAppCfg, old, "app_name=? AND channel=?", m.AppName, m.Channel)
if err != nil {
m.RowId = 0
m.Createtime = now
err = postgres.Insert(comm.TableChannelAppCfg, &m)
} else {
m.RowId = old.RowId
m.Createtime = old.Createtime
err = postgres.Save(comm.TableChannelAppCfg, &m)
}
if err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindChannelApp, "update", 0)
writeOK(c, &m)
}
// delChannelApp 删除某作用域下某渠道的配置。
func (this *serverComp) delChannelApp(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
Channel int32 `json:"channel"`
}
_ = c.ShouldBindJSON(&req)
if req.Channel == 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "channel 必填")
return
}
if err := postgres.Delete(comm.TableChannelAppCfg, "app_name=? AND channel=?", req.AppName, req.Channel); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindChannelApp, "delete", 0)
writeOK(c, gin.H{"channel": req.Channel})
}
// getChannelPayStatus 查这个应用的支付凭据配了哪几种。
//
// 渠道配置里的 PayChannels 只是「开放哪几种支付入口」,真正的商户凭据在**应用业务库**的
// app_module_config 里。两者分处不同库,运营很容易开了入口却忘了配凭据,故后台直接把结论算好:
// 遍历该 app_name 下的部署,连上任意一个业务库,读出已配置的支付模块键。
// 全局作用域(app_name='')没有对应业务库,返回空表示「无法判定」,前端据此不告警。
func (this *serverComp) getChannelPayStatus(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
}
_ = c.ShouldBindJSON(&req)
resp := gin.H{"configured": []string{}, "checked": false}
if strings.TrimSpace(req.AppName) == "" {
writeOK(c, resp)
return
}
regs := make([]*AppRegistry, 0)
if err := postgres.Find(comm.TableAppRegistry, &regs, "app_name=?", req.AppName); err != nil || len(regs) == 0 {
writeOK(c, resp)
return
}
payModules := map[string]bool{}
for _, m := range comm.PayChannelModule {
payModules[m] = true
}
for _, r := range regs {
db, err := this.module.registry.getServiceDB(r.Id)
if err != nil {
continue // 这个部署连不上,试下一个
}
rows, err := listModuleConfig(db, "")
if err != nil {
continue
}
configured := make([]string, 0, len(payModules))
seen := map[string]bool{}
for _, row := range rows {
if !payModules[row.Module] || seen[row.Module] || strings.TrimSpace(row.Value) == "" {
continue
}
seen[row.Module] = true
configured = append(configured, row.Module)
}
writeOK(c, gin.H{"configured": configured, "checked": true, "app_id": r.Id})
return
}
writeOK(c, resp)
}