代码提交

This commit is contained in:
2026-11-05 12:11:56 +08:00
parent 3d9bdb6f6e
commit 3fd3e82a01
18 changed files with 431 additions and 58 deletions
+23 -15
View File
@@ -34,6 +34,12 @@ func List(ctx context.Context, req *proto.RefundOrderListReq) (*proto.RefundOrde
if req.CID != nil && *req.CID != "" {
filter[refundOrderMod.FN.RefundOrder.CID] = *req.CID
}
if req.TransNo != nil && *req.TransNo != "" {
filter[refundOrderMod.FN.RefundOrder.TransNo] = *req.TransNo
}
if req.IP != nil && *req.IP != "" {
filter[refundOrderMod.FN.RefundOrder.IP] = *req.IP
}
if req.ChannelName != nil && *req.ChannelName != "" {
filter[refundOrderMod.FN.RefundOrder.ChannelName] = *req.ChannelName
}
@@ -60,30 +66,30 @@ func List(ctx context.Context, req *proto.RefundOrderListReq) (*proto.RefundOrde
// Add 按单个 cid 回填
func Add(ctx context.Context, req *proto.AddRefundOrderReq) (*proto.AddRefundOrderResp, error) {
return addByCids(ctx, []string{req.CID})
return addItems(ctx, []proto.RefundOrderItem{{CID: req.CID, TransNo: req.TransNo, IP: req.IP}})
}
// BatchAdd 按多个 cid 批量回填
// BatchAdd 批量回填
func BatchAdd(ctx context.Context, req *proto.BatchAddRefundOrderReq) (*proto.AddRefundOrderResp, error) {
return addByCids(ctx, req.CIDs)
return addItems(ctx, req.Items)
}
// addByCids 逐个 cid 查询 log_recharge 的成功订单,按 (appId, orderId) 判重后落库。
// 单个 cid 失败不影响其余 cid,错误直接返回由调用方决定。
func addByCids(ctx context.Context, cids []string) (*proto.AddRefundOrderResp, error) {
// addItems 逐个 cid 查询 log_recharge 的成功订单,按 (appId, orderId) 判重后落库。
// transNo 不参与查询,只随记录一起写入用于追溯。
func addItems(ctx context.Context, items []proto.RefundOrderItem) (*proto.AddRefundOrderResp, error) {
resp := &proto.AddRefundOrderResp{
Details: make([]proto.RefundCidStat, 0, len(cids)),
Details: make([]proto.RefundCidStat, 0, len(items)),
}
seen := make(map[string]bool, len(cids)) // 同一次请求里 cid 去重
for _, cid := range cids {
if cid == "" || seen[cid] {
seen := make(map[string]bool, len(items)) // 同一次请求里 cid 去重
for _, item := range items {
if item.CID == "" || seen[item.CID] {
continue
}
seen[cid] = true
seen[item.CID] = true
stat, err := addOneCid(ctx, cid)
stat, err := addOneCid(ctx, item.CID, item.TransNo, item.IP)
if err != nil {
return nil, fmt.Errorf("回填渠道[%s]失败: %w", cid, err)
return nil, fmt.Errorf("回填订单[%s]失败: %w", item.CID, err)
}
resp.Matched += stat.Matched
resp.Inserted += stat.Inserted
@@ -93,8 +99,8 @@ func addByCids(ctx context.Context, cids []string) (*proto.AddRefundOrderResp, e
return resp, nil
}
func addOneCid(ctx context.Context, cid string) (*proto.RefundCidStat, error) {
stat := &proto.RefundCidStat{CID: cid}
func addOneCid(ctx context.Context, cid, transNo, ip string) (*proto.RefundCidStat, error) {
stat := &proto.RefundCidStat{CID: cid, TransNo: transNo}
// cid 唯一标识一笔订单,只回填付款成功的那条
src, err := rechargemod.FindOneRecharge(ctx, bson.M{
@@ -125,6 +131,8 @@ func addOneCid(ctx context.Context, cid string) (*proto.RefundCidStat, error) {
doc := &refundOrderMod.RefundOrder{
AppId: src.AppId,
CID: src.CID,
TransNo: transNo,
IP: ip,
DistrictCode: src.DistrictCode,
Money: src.Money,
OrderId: src.OrderId,
+49 -1
View File
@@ -17,6 +17,7 @@ import (
"stat/statg"
"time"
"github.com/google/uuid"
"github.com/xxjwxc/public/myglobal/snowflake"
)
@@ -51,7 +52,54 @@ func GenerateConfigFile(origin, target, configFile, DC, PC string) ([]byte, stri
if err != nil {
output, _ = os.ReadFile("conf/" + configFile)
}
return output, configFile
return uniqueProfileIdentity(output, configFile), configFile
}
// profileIDNamespace 标识符派生的固定命名空间,保证同一模板每次生成的值一致
var profileIDNamespace = uuid.NewSHA1(uuid.NameSpaceURL, []byte("stat_center/mobileconfig"))
var (
rePayloadIdent = regexp.MustCompile(`(<key>PayloadIdentifier</key>[ \t]*\r?\n[ \t]*<string>)([^<]*)(</string>)`)
rePayloadUUID = regexp.MustCompile(`(<key>PayloadUUID</key>[ \t]*\r?\n[ \t]*<string>)([^<]*)(</string>)`)
reUUIDValue = regexp.MustCompile(`[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}`)
)
// uniqueProfileIdentity 把 mobileconfig 里的 PayloadIdentifier/PayloadUUID 改写成按 configFile 派生的值。
// conf 下各 app 的模板是互相复制出来的,标识符完全相同,而 iOS 按顶层 PayloadIdentifier 判重:
// 相同即替换已安装的描述文件,用户装了第二个 app 第一个就没了。
// 按 configFile 派生可保证各 app 互不覆盖;同一 app 多次下载得到相同标识符,
// 重复安装只覆盖自己,不会在桌面堆出一排图标。
func uniqueProfileIdentity(data []byte, configFile string) []byte {
if len(data) == 0 {
return data
}
derive := func(i int) string {
return uuid.NewSHA1(profileIDNamespace, []byte(fmt.Sprintf("%s#%d", configFile, i))).String()
}
n := 0
data = rePayloadUUID.ReplaceAllFunc(data, func(m []byte) []byte {
g := rePayloadUUID.FindSubmatch(m)
v := derive(n)
n++
return []byte(string(g[1]) + v + string(g[3]))
})
// 第 i 个 PayloadIdentifier 与第 i 个 PayloadUUID 共用派生值,保留原前缀(luli. / com.apple.webClip.managed.)
i := 0
data = rePayloadIdent.ReplaceAllFunc(data, func(m []byte) []byte {
g := rePayloadIdent.FindSubmatch(m)
v := derive(i)
i++
val := g[2]
if reUUIDValue.Match(val) {
val = reUUIDValue.ReplaceAll(val, []byte(v))
} else {
val = []byte(string(val) + "." + v)
}
return []byte(string(g[1]) + string(val) + string(g[3]))
})
return data
}
func GetAppConfig(appId int32) (origin, target, config string) {
+90
View File
@@ -0,0 +1,90 @@
package versionSer
import (
"os"
"path/filepath"
"regexp"
"testing"
)
var reAllIdent = regexp.MustCompile(`<key>Payload(?:Identifier|UUID)</key>[ \t]*\r?\n[ \t]*<string>([^<]*)</string>`)
func collectIdentity(t *testing.T, data []byte) []string {
t.Helper()
var out []string
for _, m := range reAllIdent.FindAllSubmatch(data, -1) {
out = append(out, string(m[1]))
}
return out
}
// TestUniqueProfileIdentityMatchesTemplates 确认 uniqueProfileIdentity 的派生结果与 conf 下
// 已改好的模板完全一致:两边用同一套规则,部署后不会再变一次标识符。
func TestUniqueProfileIdentityMatchesTemplates(t *testing.T) {
files, err := filepath.Glob("../../conf/*.mobileconfig")
if err != nil || len(files) == 0 {
t.Fatalf("找不到 mobileconfig 模板: %v", err)
}
seen := make(map[string]string)
for _, path := range files {
name := filepath.Base(path)
raw, err := os.ReadFile(path)
if err != nil {
t.Fatalf("%s 读取失败: %v", name, err)
}
want := collectIdentity(t, raw)
if len(want) == 0 {
t.Fatalf("%s 没有解析到任何标识符", name)
}
got := collectIdentity(t, uniqueProfileIdentity(raw, name))
if len(got) != len(want) {
t.Fatalf("%s 标识符数量不一致: got %d want %d", name, len(got), len(want))
}
for i := range want {
if got[i] != want[i] {
t.Errorf("%s 第 %d 个标识符不一致:\n got %s\n want %s", name, i, got[i], want[i])
}
}
// 跨文件判重:顶层 PayloadIdentifier 相同会让 iOS 覆盖安装
for _, v := range got {
if prev, ok := seen[v]; ok {
t.Errorf("标识符 %s 同时出现在 %s 和 %s,iOS 上会互相覆盖", v, prev, name)
}
seen[v] = name
}
}
}
// TestUniqueProfileIdentityIsStable 同一模板多次生成必须得到相同标识符,
// 否则用户每次下载都会在桌面多出一个图标。
func TestUniqueProfileIdentityIsStable(t *testing.T) {
raw, err := os.ReadFile("../../conf/hgmj.mobileconfig")
if err != nil {
t.Skipf("模板不存在: %v", err)
}
a := collectIdentity(t, uniqueProfileIdentity(raw, "hgmj.mobileconfig"))
b := collectIdentity(t, uniqueProfileIdentity(raw, "hgmj.mobileconfig"))
for i := range a {
if a[i] != b[i] {
t.Fatalf("第 %d 个标识符不稳定: %s != %s", i, a[i], b[i])
}
}
}
// TestUniqueProfileIdentityDiffersByConfigFile 不同 configFile 必须派生出不同标识符。
func TestUniqueProfileIdentityDiffersByConfigFile(t *testing.T) {
raw, err := os.ReadFile("../../conf/hgmj.mobileconfig")
if err != nil {
t.Skipf("模板不存在: %v", err)
}
a := collectIdentity(t, uniqueProfileIdentity(raw, "hgmj.mobileconfig"))
b := collectIdentity(t, uniqueProfileIdentity(raw, "newapp.mobileconfig"))
for i := range a {
if a[i] == b[i] {
t.Fatalf("不同 configFile 派生出相同标识符: %s", a[i])
}
}
}