代码提交
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user