Browse Source

harmonyos, 广告,重置充值双倍

master
linquan 2 weeks ago
parent
commit
5ccacde1b7
  1. 2
      api/v1/game/basicinfo.go
  2. 125
      api/v1/game/order.go
  3. 15
      internal/controller/game_order.go
  4. 5
      internal/controller/game_server.go
  5. 74
      internal/harmonyos/iap_server.go
  6. 271
      internal/harmonyos/jws_checker.go
  7. 126
      internal/harmonyos/jwt_generator.go
  8. 79
      internal/harmonyos/notification/appserver.go
  9. 46
      internal/harmonyos/notification/notificationConstant.go
  10. 31
      internal/harmonyos/notification/notificationMetaData.go
  11. 27
      internal/harmonyos/notification/notificationPayload.go
  12. 107
      internal/harmonyos/order_lookup.go
  13. 87
      internal/harmonyos/order_service.go
  14. 84
      internal/harmonyos/subscription_service.go
  15. 123
      internal/service/harmonyOrder.go
  16. 1
      internal/service/hmsAccessToken.go
  17. 4
      internal/serviceGame/advertisement.go
  18. 145
      internal/serviceGame/internal/advertisement.go
  19. 44
      internal/serviceGame/order.go
  20. 6
      manifest/config/IAPKey.p8
  21. 14
      manifest/config/RootCaG2Ecdsa.cer
  22. 5
      test/build.bat
  23. 126
      test/recharge.go
  24. 331
      test/test.go

2
api/v1/game/basicinfo.go

@ -254,7 +254,7 @@ type GameChangeNameRes struct {
type HarmonyChangeEventReq struct {
g.Meta `path:"/harmonyChangeEvent" tags:"game" method:"post" summary:"鸿蒙账号改变事件"`
Account string `p:"account"`
Account string `p:"user_id"`
Server int `p:"server"`
Name string `p:"name"`
}

125
api/v1/game/order.go

@ -252,6 +252,7 @@ type AddRechargeSignRes struct {
type ResetRechargeSignReq struct {
g.Meta `path:"/order/resetSign" tags:"订单" method:"get" summary:"重置充值情况"`
Password string `p:"password"`
Server int32 `p:"serverId"`
}
type ResetRechargeSignRes struct {
@ -304,6 +305,7 @@ type HuaWeiOrderResponse struct {
ContinuationToken string `json:"continuationToken"`
OrderInfoList []HuaWeiOrderInfo `json:"orderInfoList"`
}
type HuaWeiOrderInfo struct {
OrderNo string `json:"orderNo"`
RequestId string `json:"requestId"`
@ -331,3 +333,126 @@ type GetHuaWeiOrderListRes struct {
g.Meta `mime:"application/json"`
Order HuaWeiOrderResponse `json:"order"`
}
// 获取订单号
type GetHarmonyOrderReq struct {
g.Meta `path:"/order/getHarmonyOrder" tags:"订单" method:"get" summary:"获取订单号"`
Order string `p:"purchaseOrderId"`
}
type GetHarmonyOrderRes struct {
g.Meta `mime:"application/json"`
OrderStatus int32 `json:"orderStatus"`
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
PurchaseOrderInfo PurchaseOrderPayload `json:"purchaseOrderInfo"`
}
type HarmonyLookup struct {
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
OrderStatus int32 `json:"orderStatus"`
JwsPurchaseOrder string `json:"jwsPurchaseOrder"`
}
type PurchaseOrder struct {
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
JwsPurchaseOrder string `json:"jwsPurchaseOrder"`
}
type DeveloperPayload struct {
UnitId int64 `json:"unitId"`
Server int `json:"server"`
Channel string `json:"channel"`
TradeNo string `json:"tradeNo"`
}
type PurchaseOrderPayload struct {
Environment string `json:"environment"`
PurchaseOrderId string `json:"purchaseOrderId"`
PurchaseToken string `json:"purchaseToken"`
ApplicationId string `json:"applicationId"`
ProductId string `json:"productId"`
ProductType string `json:"productType"`
Quantity int64 `json:"quantity"`
PurchaseTime int64 `json:"purchaseTime"`
FinishStatus string `json:"finishStatus"`
NeedFinish bool `json:"needFinish"`
Price int64 `json:"price"`
Currency string `json:"currency"`
DeveloperPayload string `json:"developerPayload"`
PurchaseOrderRevocationReasonCode string `json:"purchaseOrderRevocationReasonCode"`
RevocationTime int64 `json:"revocationTime"`
OfferTypeCode string `json:"offerTypeCode"`
OfferId string `json:"offerId"`
CountryCode string `json:"countryCode"`
SignedTime int64 `json:"signedTime"`
}
// 获取订单号
type GetHarmonyOrdersReq struct {
g.Meta `path:"/order/getHarmonyOrders" tags:"订单" method:"get" summary:"获取订单号"`
StartTime int64 `p:"startTime"`
EndTime int64 `p:"endTime"`
ContinuationToken string `p:"continuationToken"`
}
type GetHarmonyOrdersRes struct {
g.Meta `mime:"application/json"`
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
OrderInfoList []OrderInfo `json:"orderInfoList"`
ContinuationToken string `json:"continuationToken"`
}
type HarmonyOrders struct {
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
OrderInfoList []OrderInfo `json:"orderInfoList"`
ContinuationToken string `json:"continuationToken"`
}
type OrderInfo struct {
RequestId string `json:"requestId"`
Country string `json:"country"`
MerchantId string `json:"merchantId"`
ApplicationId string `json:"applicationId"`
OrderTime int64 `json:"orderTime"`
TradeTime int64 `json:"tradeTime"`
ProductId string `json:"productId"`
ProductName string `json:"productName"`
PayMoney string `json:"payMoney"`
CouponAmt string `json:"couponAmt"`
Currency string `json:"currency"`
TradeState int32 `json:"tradeState"`
TradeType string `json:"tradeType"`
RefundTime int64 `json:"refundTime"`
RevocationTime string `json:"refundMoney"`
}
// 获取订单号
type GetHarmonyUserOrdersReq struct {
g.Meta `path:"/order/getHarmonyUserOrders" tags:"订单" method:"get" summary:"获取订单号"`
PurchaseOrderId string `p:"purchaseOrderId"`
ProductType string `p:"productType"`
ContinuationToken string `p:"continuationToken"`
StartTime int64 `p:"startTime"`
EndTime int64 `p:"endTime"`
ProductIdList []string `p:"productIdList"`
}
type GetHarmonyUserOrdersRes struct {
g.Meta `mime:"application/json"`
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
OrderInfoList []PurchaseOrderPayload `json:"orderInfoList"`
ContinuationToken string `json:"continuationToken"`
}
type HarmonyUserOrders struct {
ResponseCode string `json:"responseCode"`
ResponseMessage string `json:"responseMessage"`
ContinuationToken string `json:"continuationToken"`
JwsPurchaseOrderList []string `json:"jwsPurchaseOrderList"`
}

15
internal/controller/game_order.go

@ -100,3 +100,18 @@ func (c *orderController) GetHuaWeiOrderList(ctx context.Context, req *game.GetH
res, err = serviceGame.GameOrder().GetHuaWeiOrderList(ctx, req)
return
}
func (c *orderController) GetHarmonyOrder(ctx context.Context, req *game.GetHarmonyOrderReq) (res *game.GetHarmonyOrderRes, err error) {
res, err = serviceGame.GameOrder().GetHarmonyOrder(ctx, req)
return
}
func (c *orderController) GetHarmonyOrders(ctx context.Context, req *game.GetHarmonyOrdersReq) (res *game.GetHarmonyOrdersRes, err error) {
res, err = serviceGame.GameOrder().GetHarmonyOrders(ctx, req)
return
}
func (c *orderController) GetHarmonyUserOrders(ctx context.Context, req *game.GetHarmonyUserOrdersReq) (res *game.GetHarmonyUserOrdersRes, err error) {
res, err = serviceGame.GameOrder().GetHarmonyUserOrders(ctx, req)
return
}

5
internal/controller/game_server.go

@ -157,3 +157,8 @@ func (c *serverController) HarmonyChangeEvent(ctx context.Context, req *game.Har
res, err = serviceGame.GameRole().HarmonyChangeEvent(ctx, req)
return
}
func (c *serverController) Csj(ctx context.Context, req *game.HarmonyChangeEventReq) (res *game.HarmonyChangeEventRes, err error) {
res, err = serviceGame.GameRole().HarmonyChangeEvent(ctx, req)
return
}

74
internal/harmonyos/iap_server.go

@ -0,0 +1,74 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package harmonyos
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
type IapServer struct {
}
const (
// URL_ROOT IAP Server Root Url
URL_ROOT = "https://iap.cloud.huawei.com"
TIME_OUT = time.Second * 5 // TODO: Need to replace it with the actual business logic.
WHITE_SPACE = " "
)
func (iap *IapServer) httpPost(url string, bodyMap map[string]interface{}) (string, error) {
bodyData, err := json.Marshal(bodyMap)
if err != nil {
return "", err
}
req, err := http.NewRequest("POST", url, bytes.NewReader(bodyData))
if err != nil {
return "", err
}
jwtGenerator := &JWTGenerator{}
jwt, err := jwtGenerator.GenJWT(bodyMap)
if err != nil {
return "", err
}
req, err = iap.BuildAuthorization(req, jwt)
var RequestHttpClient = http.Client{Timeout: TIME_OUT}
response, err := RequestHttpClient.Do(req)
if err != nil {
return "", err
}
bodyBytes, err := ioutil.ReadAll(response.Body)
return string(bodyBytes), err
}
func (iap *IapServer) BuildAuthorization(req *http.Request, jwt string) (*http.Request, error) {
var authHeader = fmt.Sprintf("Bearer%s%s", WHITE_SPACE, jwt)
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
req.Header.Set("Authorization", authHeader)
return req, nil
}

271
internal/harmonyos/jws_checker.go

@ -0,0 +1,271 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package harmonyos
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"github.com/cristalhq/jwt/v3"
"io/ioutil"
"net/http"
"strings"
)
const (
CA_CERT_FILE_PATH = "manifest/config/RootCaG2Ecdsa.cer" // TODO: Need to replace it with the actual value.
LEAF_CERT_OID = "1.3.6.1.4.1.2011.2.415.1.1"
// HEADER_PARAM_ALG Algorithm type. The value is always ES256.
HEADER_PARAM_ALG = "ES256"
X5C_CHAIN_LENGTH = 3
)
type JWSChecker struct {
}
type Header struct {
// Alg Algorithm type. The value is always ES256.
Alg string `json:"alg"`
// X5c Indicates the X.509 certificate chain. The sequence is leaf certificate, intermediate certificate, and root certificate.
X5c []string `json:"x5c"`
}
func (jwsChecker *JWSChecker) CheckAndDecodeJWS(jwsStr string) (string, error) {
jwsStrArr := strings.Split(jwsStr, ".")
if len(jwsStrArr) != X5C_CHAIN_LENGTH {
// TODO: Need to replace it with the actual business logic.
return "", errors.New("invalid jwsStr length")
}
certChain, err := jwsChecker.extractCertChain(jwsStrArr)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
rootCert, err := jwsChecker.loadRootCert()
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
if err = jwsChecker.verifyCertChain(certChain, rootCert); err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
if err = jwsChecker.verifyOID(certChain[0]); err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
if err = jwsChecker.verifyCRLFromCerts(certChain[:2]); err != nil {
// TODO: Determine whether to ignore CRL certificate download exception based on application Security Policy.
return "", err
}
if err = jwsChecker.verifySignature(jwsStr, certChain[0], string(jwt.ES256)); err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
payLoadBytes, err := base64.RawStdEncoding.DecodeString(jwsStrArr[1])
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
return string(payLoadBytes), err
}
func (jwsChecker *JWSChecker) loadRootCert() (*x509.Certificate, error) {
rootCABytes, err := ioutil.ReadFile(CA_CERT_FILE_PATH)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
rootCerBlock, _ := pem.Decode(rootCABytes)
if rootCerBlock == nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
rootCer, err := x509.ParseCertificate(rootCerBlock.Bytes)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
return rootCer, err
}
func (jwsChecker *JWSChecker) extractCertChain(jwsStrArr []string) ([]*x509.Certificate, error) {
headerByte, err := base64.RawStdEncoding.DecodeString(jwsStrArr[0])
if err != nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
var header Header
err = json.Unmarshal(headerByte, &header)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
certChain := make([]*x509.Certificate, len(header.X5c))
for i := range header.X5c {
certByte, err := base64.StdEncoding.DecodeString(header.X5c[i])
if err != nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
cert, err := x509.ParseCertificate(certByte)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return nil, err
}
certChain[i] = cert
}
return certChain, err
}
func (jwsChecker *JWSChecker) verifyCertChain(certChain []*x509.Certificate, rootCert *x509.Certificate) error {
roots := x509.NewCertPool()
roots.AddCert(rootCert)
intermedia := x509.NewCertPool()
intermedia.AddCert(certChain[1])
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: intermedia,
}
_, err := rootCert.Verify(opts)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return err
}
_, err = certChain[0].Verify(opts)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return err
}
return err
}
func (jwsChecker *JWSChecker) verifyCRLFromCerts(certChain []*x509.Certificate) error {
for _, cert := range certChain {
err := checkCrl(cert)
if err != nil {
// TODO: Determine whether to ignore CRL certificate download exception based on application Security Policy.
return err
}
}
return nil
}
func checkCrl(cert *x509.Certificate) error {
if len(cert.CRLDistributionPoints) == 0 {
return errors.New("No CRL distribution points found in certificate")
}
resp, err := http.Get(cert.CRLDistributionPoints[0])
if err != nil {
// TODO: Determine whether to ignore CRL certificate download exception based on application Security Policy.
return err
}
defer resp.Body.Close()
crlData, err := ioutil.ReadAll(resp.Body)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return err
}
crl, err := x509.ParseCRL(crlData)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return err
}
if crl.TBSCertList.RevokedCertificates == nil {
return nil
}
for _, revokedCert := range crl.TBSCertList.RevokedCertificates {
if revokedCert.SerialNumber.Cmp(cert.SerialNumber) == 0 {
// TODO: Need to replace it with the actual business logic.
return fmt.Errorf("certificate %s has been revoked", cert.Subject.CommonName)
}
}
return nil
}
func (jwsChecker *JWSChecker) verifyOID(leafCert *x509.Certificate) error {
var isExist = false
for i := range leafCert.Extensions {
if LEAF_CERT_OID == leafCert.Extensions[i].Id.String() && leafCert.Extensions[i].Critical == false {
isExist = true
}
}
if !isExist {
// TODO: Need to replace it with the actual business logic.
return errors.New("oid verification failed")
}
return nil
}
func (jwsChecker *JWSChecker) verifySignature(jwsStr string, leafCert *x509.Certificate, algParam string) error {
if algParam != jwt.ES256.String() {
// TODO: Need to replace it with the actual business logic.
return errors.New("invalid algorithm")
}
switch pubicKey := leafCert.PublicKey.(type) {
case *ecdsa.PublicKey:
verifier, err := jwt.NewVerifierES(jwt.ES256, pubicKey)
if err != nil {
return err
}
token := []byte(jwsStr)
_, err = jwt.ParseAndVerify(token, verifier)
if err != nil {
return err
}
default:
// TODO: Need to replace it with the actual business logic.
return errors.New("public key not supported")
}
return nil
}

126
internal/harmonyos/jwt_generator.go

@ -0,0 +1,126 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package harmonyos
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"github.com/cristalhq/jwt/v3"
"io/ioutil"
"time"
)
type JWTGenerator struct {
}
type IAPJWTClaims struct {
// Iss Key issuer ID.
Iss string `json:"iss"`
// Aud Expected receiver of the JWT. The value is fixed at iap-v1.
Aud string `json:"aud"`
// Iat Time when the JWT is issued. The value is a UTC timestamp, in seconds.
Iat int64 `json:"iat"`
// Exp Time when the JWT expires. The value is a UTC timestamp, in seconds. exp-iat indicates the validity period of the JWT, which cannot exceed one hour.
Exp int64 `json:"exp"`
// Aid App ID.
Aid string `json:"aid"`
// Digest Hash value of the request body (JSON character string), which is used to verify the integrity of the body. The algorithm is SHA-256.
Digest string `json:"digest"`
}
const (
// JWT_PRI_KEY_PATH Private key file path.
JWT_PRI_KEY_PATH = "manifest/config/IAPKey.p8" // TODO: Need to replace it with the actual value.
// ACTIVE_TIME JWT validity period, which is a UTC timestamp in seconds. The validity period cannot exceed 1 hour.
ACTIVE_TIME_SECOND = 3600 // TODO: Need to replace it with the actual value.
KID = "f35b90cf-48be-4bb9-bb66-f7e814d5521c" // TODO: Need to replace it with the actual value.
ISSUER_ID = "002124f1-f9a9-468e-92ea-26561ea9c605" // TODO: Need to replace it with the actual business logic.
APP_ID = "6917589392953468039" // TODO: Need to replace it with the actual business logic.
)
func (jwtGenerator *JWTGenerator) GenJWT(bodyMap map[string]interface{}) (string, error) {
privateKeyPEM, err := ioutil.ReadFile(JWT_PRI_KEY_PATH)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
block, _ := pem.Decode(privateKeyPEM)
if block == nil {
// TODO: Need to replace it with the actual business logic.
return "", errors.New("the key content is empty")
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
ecdsaPrivateKey, ok := privateKey.(*ecdsa.PrivateKey)
if !ok {
// TODO: Need to replace it with the actual business logic.
return "", errors.New("failed to convert private key to *ecdsa.PrivateKey")
}
signer, err := jwt.NewSignerES(jwt.ES256, ecdsaPrivateKey)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
payloadJson, err := json.Marshal(bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
return "", err
}
hash := sha256.New()
hash.Write(payloadJson)
sha256Sum := hash.Sum(nil)
sha256Hex := hex.EncodeToString(sha256Sum)
builder := jwt.NewBuilder(signer, jwt.WithKeyID(KID)) // TODO: Need to replace it with the actual business logic.
signTime := time.Now().UTC().Unix()
claims := IAPJWTClaims{
Iss: ISSUER_ID, // TODO: Need to replace it with the actual business logic.
Aud: "iap-v1",
Iat: signTime,
Exp: signTime + ACTIVE_TIME_SECOND,
Aid: APP_ID, // TODO: Need to replace it with the actual business logic.
Digest: sha256Hex,
}
iapJwt, err := builder.Build(claims)
if err != nil {
// TODO: Need replace it with your business logic.
return "", err
}
return iapJwt.String(), nil
}

79
internal/harmonyos/notification/appserver.go

@ -0,0 +1,79 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package notification
import (
"encoding/json"
)
type AppServer struct{}
// DealNotificationV3 Deal notification information V3.
// Params:
//
// notificationPayloadStr: Notification content, which is the payload decoded by jwsNotification.
//
// Return:
//
// nil
//
// Exception:
//
// error
func (a *AppServer) DealNotificationV3(notificationPayloadStr string) error {
var notificationPayload = NotificationPayload{}
if err := json.Unmarshal([]byte(notificationPayloadStr), &notificationPayload); err != nil {
return err
}
// TODO: Need to replace it with the actual business logic.
notificationSubtype := notificationPayload.NotificationSubtype
switch notificationSubtype {
case INITIAL_BUY:
break
case DID_RENEW:
break
case RESTORE:
break
case AUTO_RENEW_ENABLED:
break
case AUTO_RENEW_DISABLED:
break
case DOWNGRADE:
break
case UPGRADE:
break
case REFUND_TRANSACTION:
break
case BILLING_RETRY:
break
case PRICE_INCREASE:
break
case BILLING_RECOVERY:
break
case PRODUCT_NOT_FOR_SALE:
break
case APPLICATION_DELETE_SUBSCRIPTION_HOSTING:
break
case RENEWAL_EXTENDED:
break
default:
break
}
return nil
}

46
internal/harmonyos/notification/notificationConstant.go

@ -0,0 +1,46 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package notification
type NotificationType = string
type SubNotificationType = string
const (
DID_NEW_TRANSACTION NotificationType = "DID_NEW_TRANSACTION"
DID_CHANGE_RENEWAL_STATUS NotificationType = "DID_CHANGE_RENEWAL_STATUS"
REVOKE NotificationType = "REVOKE"
RENEWAL_TIME_MODIFIED NotificationType = "RENEWAL_TIME_MODIFIED"
EXPIRE NotificationType = "EXPIRE"
SYNC NotificationType = "SYNC"
INITIAL_BUY SubNotificationType = "INITIAL_BUY"
DID_RENEW SubNotificationType = "DID_RENEW"
RESTORE SubNotificationType = "RESTORE"
AUTO_RENEW_ENABLED SubNotificationType = "AUTO_RENEW_ENABLED"
AUTO_RENEW_DISABLED SubNotificationType = "AUTO_RENEW_DISABLED"
DOWNGRADE SubNotificationType = "DOWNGRADE"
UPGRADE SubNotificationType = "UPGRADE"
REFUND_TRANSACTION SubNotificationType = "REFUND_TRANSACTION"
BILLING_RETRY SubNotificationType = "BILLING_RETRY"
PRICE_INCREASE SubNotificationType = "PRICE_INCREASE"
BILLING_RECOVERY SubNotificationType = "BILLING_RECOVERY"
PRODUCT_NOT_FOR_SALE SubNotificationType = "PRODUCT_NOT_FOR_SALE"
APPLICATION_DELETE_SUBSCRIPTION_HOSTING SubNotificationType = "APPLICATION_DELETE_SUBSCRIPTION_HOSTING"
RENEWAL_EXTENDED SubNotificationType = "RENEWAL_EXTENDED"
)

31
internal/harmonyos/notification/notificationMetaData.go

@ -0,0 +1,31 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package notification
type NotificationMetaData struct {
Environment string `json:"environment"`
ApplicationId string `json:"applicationId"`
PackageName string `json:"packageName"`
Type int `json:"type"`
CurrentProductId string `json:"currentProductId"`
SubGroupId string `json:"subGroupId"`
SubGroupGenerationId string `json:"subGroupGenerationId"`
SubscriptionId string `json:"subscriptionId"`
PurchaseToken string `json:"purchaseToken"`
PurchaseOrderId string `json:"purchaseOrderId"`
}

27
internal/harmonyos/notification/notificationPayload.go

@ -0,0 +1,27 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package notification
type NotificationPayload struct {
NotificationType NotificationType `json:"notificationType"`
NotificationSubtype SubNotificationType `json:"notificationSubtype"`
NotificationRequestId string `json:"notificationRequestId"`
NotificationMetaData NotificationMetaData `json:"notificationMetaData"`
NotificationVersion string `json:"notificationVersion"`
SignedTime int64 `json:"signedTime"`
}

107
internal/harmonyos/order_lookup.go

@ -0,0 +1,107 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package harmonyos
import (
"log"
)
const (
URL_ORDER_LOOKUP = "/harmony/v1/application/order/lookup"
URL_ORDER_LIST_QUERY = "/order/harmony/v1/application/trade/orders/query"
URL_USER_ORDER_LIST_QUERY = "/harmony/v1/application/user/orders/query"
)
type OrderLookup struct {
IapServer
}
// OrderQuery This API is used to query the latest status of a consumable or non-consumable order.
// Params:
//
// orderNo: Order ID of a purchase.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (orderService *OrderLookup) OrderQuery(bodyMap map[string]interface{}) (resp string, err error) {
resp, err = orderService.httpPost(URL_ROOT+URL_ORDER_LOOKUP, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
log.Print("Error:", err)
} else {
// TODO: Need to replace it with the actual business logic.
log.Print("order query response is: ", resp)
}
return resp, err
}
// UserOrdersQuery This API is used to query the latest status of a consumable or non-consumable order.
// Params:
//
// purchaseOrderId: Order ID of a purchase.
// productType: product Type.
// continuationToken: continuationToken.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (orderService *OrderLookup) UserOrdersQuery(bodyMap map[string]interface{}) (resp string, err error) {
resp, err = orderService.httpPost(URL_ROOT+URL_USER_ORDER_LIST_QUERY, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
log.Print("Error:", err)
} else {
// TODO: Need to replace it with the actual business logic.
log.Print("order status query response is: ", resp)
}
return resp, err
}
// OrdersQuery This API is used to query the latest status of a consumable or non-consumable order.
// Params:
//
// orderNo: Order ID of a purchase.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (orderService *OrderLookup) OrdersQuery(bodyMap map[string]interface{}) (resp string, err error) {
resp, err = orderService.httpPost(URL_ROOT+URL_ORDER_LIST_QUERY, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
log.Print("Error:", err)
} else {
// TODO: Need to replace it with the actual business logic.
log.Print("order status query response is: ", resp)
}
return resp, err
}

87
internal/harmonyos/order_service.go

@ -0,0 +1,87 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package harmonyos
import (
"log"
)
const (
URL_ORDER_STATUS_QUERY = "/order/harmony/v1/application/order/status/query"
URL_ORDER_SHIPPED_CONFIRM = "/order/harmony/v1/application/purchase/shipped/confirm"
)
type OrderService struct {
IapServer
}
// OrderStatusQuery This API is used to query the latest status of a consumable or non-consumable order.
// Params:
//
// purchaseOrderId: Order ID of a purchase.
// purchaseToken: Purchase token of a product, which you can obtain from the information returned after a purchase or an order query.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (orderService *OrderService) OrderStatusQuery(purchaseOrderId string, purchaseToken string) (resp string, err error) {
bodyMap := make(map[string]interface{})
bodyMap["purchaseOrderId"] = purchaseOrderId
bodyMap["purchaseToken"] = purchaseToken
resp, err = orderService.httpPost(URL_ROOT+URL_ORDER_STATUS_QUERY, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
log.Print("Error:", err)
} else {
// TODO: Need to replace it with the actual business logic.
log.Print("order status query response is: ", resp)
}
return resp, err
}
// OrderShippedConfirm This API is used to acknowledge that a purchased consumable or non-consumable has been delivered.
// Params:
//
// purchaseOrderId: Order ID of a purchase.
// purchaseToken: Purchase token of a product, which you can obtain from the information returned after a purchase or an order query.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (orderService *OrderService) OrderShippedConfirm(purchaseOrderId string, purchaseToken string) {
bodyMap := make(map[string]interface{})
bodyMap["purchaseOrderId"] = purchaseOrderId
bodyMap["purchaseToken"] = purchaseToken
resp, err := orderService.httpPost(URL_ROOT+URL_ORDER_SHIPPED_CONFIRM, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
log.Print("Error:", err)
}
// TODO: Need to replace it with the actual business logic.
log.Print("order shipped confirm response is: ", resp)
}

84
internal/harmonyos/subscription_service.go

@ -0,0 +1,84 @@
/*
* Copyright 2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package harmonyos
import (
"fmt"
)
const (
URL_SUB_STATUS_QUERY = "/subscription/harmony/v1/application/subscription/status/query"
URL_SUB_SHIPPED_CONFIRM = "/subscription/harmony/v1/application/purchase/shipped/confirm"
)
type SubscriptionService struct {
IapServer
}
// SubStatusQuery This API is used to query the latest status of an auto-renewable subscription.
// Params:
//
// purchaseOrderId: Order ID of a purchase.
// purchaseToken: Purchase token of a product, which you can obtain from the information returned after a purchase or an order query.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (subService *SubscriptionService) SubStatusQuery(purchaseOrderId string, purchaseToken string) {
bodyMap := make(map[string]interface{})
bodyMap["purchaseOrderId"] = purchaseOrderId
bodyMap["purchaseToken"] = purchaseToken
resp, err := subService.httpPost(URL_ROOT+URL_SUB_STATUS_QUERY, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
fmt.Println("Error:", err)
}
// TODO: Need to replace it with the actual business logic.
fmt.Println("sub status query response is:", resp)
}
// SubShippedConfirm This API is used to acknowledge that a purchased subscription has been delivered to the user.
// Params:
//
// purchaseOrderId: Order ID of a purchase.
// purchaseToken: Purchase token of a product, which you can obtain from the information returned after a purchase or an order query.
//
// Return:
//
// nil
//
// Exception:
//
// nil
func (subService *SubscriptionService) SubShippedConfirm(purchaseOrderId string, purchaseToken string) {
// pack the request body
bodyMap := make(map[string]interface{})
bodyMap["purchaseOrderId"] = purchaseOrderId
bodyMap["purchaseToken"] = purchaseToken
resp, err := subService.httpPost(URL_ROOT+URL_SUB_SHIPPED_CONFIRM, bodyMap)
if err != nil {
// TODO: Need to replace it with the actual business logic.
}
// TODO: Need to replace it with the actual business logic.
fmt.Println("sub shipped confirm response is:", resp)
}

123
internal/service/harmonyOrder.go

@ -0,0 +1,123 @@
package service
import (
"encoding/json"
"errors"
"log"
"strconv"
"tyj_admin/api/v1/game"
"tyj_admin/internal/harmonyos"
)
func HarmonyOrderLookup(req *game.GetHarmonyOrderReq) (res *game.GetHarmonyOrderRes, err error) {
res = new(game.GetHarmonyOrderRes)
bodyMap := make(map[string]interface{})
bodyMap["orderNo"] = req.Order
orderService := &harmonyos.OrderLookup{}
orderInfoStr, err := orderService.OrderQuery(bodyMap)
if err != nil {
log.Printf("HarmonyOrderLookup======>.OrderStatusQuery : %v - err:%s", req.Order, err.Error())
return
}
orderInfo := &game.HarmonyLookup{}
if err = json.Unmarshal([]byte(orderInfoStr), &orderInfo); err != nil {
log.Printf("HarmonyOrderLookup======>.Unmarshal : %v", orderInfoStr)
return
}
res.OrderStatus = orderInfo.OrderStatus
if orderInfo.OrderStatus != 1 {
err = errors.New("交易号无效")
return
}
jwsChecker := &harmonyos.JWSChecker{}
purchaseOrderStr, err := jwsChecker.CheckAndDecodeJWS(orderInfo.JwsPurchaseOrder)
if err != nil {
log.Printf("HarmonyOrderLookup======>.jwsChecker.CheckAndDecodeJWS : err: %s, %s", err.Error(), purchaseOrderStr)
return
}
purchaseOrder := game.PurchaseOrderPayload{}
if err = json.Unmarshal([]byte(purchaseOrderStr), &purchaseOrder); err != nil {
log.Printf("HarmonyOrderLookup======>.CheckAndDecodeJWS Unmarshal: %v, Err: %s", purchaseOrderStr, err)
return
}
res.PurchaseOrderInfo = purchaseOrder
return
}
func HarmonyOrders(req *game.GetHarmonyOrdersReq) (res *game.GetHarmonyOrdersRes, err error) {
res = new(game.GetHarmonyOrdersRes)
bodyMap := map[string]interface{}{
"startTime": req.StartTime,
"endTime": req.EndTime,
}
if len(req.ContinuationToken) > 0 {
bodyMap["continuationToken"] = req.ContinuationToken
}
orderService := &harmonyos.OrderLookup{}
orderInfoStr, err := orderService.OrdersQuery(bodyMap)
if err != nil {
log.Printf("HarmonyOrders======>.StartTime: %d, EndTime : %d - err:%s", req.StartTime, req.EndTime, err.Error())
return
}
if err = json.Unmarshal([]byte(orderInfoStr), &res); err != nil {
log.Printf("HarmonyOrders======>.Unmarshal : %v", orderInfoStr)
return
}
return
}
func HarmonyUserOrders(req *game.GetHarmonyUserOrdersReq) (res *game.GetHarmonyUserOrdersRes, err error) {
res = new(game.GetHarmonyUserOrdersRes)
bodyMap := make(map[string]interface{})
bodyMap["purchaseOrderId"] = req.PurchaseOrderId
if req.ProductType != "" {
bodyMap["productType"], err = strconv.Atoi(req.ProductType)
}
if req.StartTime != 0 {
bodyMap["startTime"] = req.StartTime
}
if req.EndTime != 0 {
bodyMap["endTime"] = req.EndTime
}
if len(req.ProductIdList) > 0 {
bodyMap["productIdList"] = req.ProductIdList
}
if len(req.ContinuationToken) > 0 {
bodyMap["continuationToken"] = req.ContinuationToken
}
orderService := &harmonyos.OrderLookup{}
orderInfoStr, err := orderService.UserOrdersQuery(bodyMap)
if err != nil {
log.Printf("HarmonyUserOrders======>.purchaseOrderId: %s, - err:%s", req.PurchaseOrderId, err.Error())
return
}
orderInfo := game.HarmonyUserOrders{}
if err = json.Unmarshal([]byte(orderInfoStr), &orderInfo); err != nil {
log.Printf("HarmonyUserOrders======>.Unmarshal : %v", orderInfoStr)
return
}
res.ResponseCode = orderInfo.ResponseCode
res.ResponseMessage = orderInfo.ResponseMessage
res.ContinuationToken = orderInfo.ContinuationToken
for _, v := range orderInfo.JwsPurchaseOrderList {
jwsChecker := &harmonyos.JWSChecker{}
purchaseOrderStr, err1 := jwsChecker.CheckAndDecodeJWS(v)
if err1 != nil {
err = err1
log.Printf("HarmonyUserOrders======>.jwsChecker.CheckAndDecodeJWS : err: %s, %s", err.Error(), purchaseOrderStr)
continue
}
purchaseOrder := game.PurchaseOrderPayload{}
if err = json.Unmarshal([]byte(purchaseOrderStr), &purchaseOrder); err != nil {
log.Printf("HarmonyUserOrders======>.CheckAndDecodeJWS Unmarshal: %v, Err: %s", purchaseOrderStr, err)
continue
}
res.OrderInfoList = append(res.OrderInfoList, purchaseOrder)
}
return
}

1
internal/service/hmsAccessToken.go

@ -16,6 +16,7 @@ type DemoConfig struct {
TokenUrl string
RootUrlOrder string
AccessToken string
UrlOrderLookup string
}
func GetDefaultConfig() *DemoConfig {

4
internal/serviceGame/advertisement.go

@ -52,13 +52,13 @@ func (g *advertisementImpl) DeepAdvertise(ctx context.Context, req *game.DeepADR
func (g *advertisementImpl) AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.ATHARes, err error) {
res = new(game.ATHARes)
res, err = internal.AttributionHugeAmount(ctx, req)
_, err = internal.AttributionHugeAmount(ctx, req)
return
}
func (g *advertisementImpl) ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSHARes, err error) {
res = new(game.CSHARes)
res, err = internal.ConversionHugeAmount(ctx, req)
res, err = internal.ConversionHugeAmount(ctx, req, entity.AdvertisementOceanegine1{})
return
}

145
internal/serviceGame/internal/advertisement.go

@ -104,69 +104,25 @@ func sendMsgHugeAmount(ctx context.Context, url string, data map[string]interfac
if err != nil {
return "", err
}
fmt.Println("Deposit - json: ", tmp)
log.Println("Deposit - json: ", url, tmp)
return tmp, err
}
func AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.ATHARes, err error) {
func AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res entity.AdvertisementOceanegine1, err error) {
log.Printf("AttributionHugeAmount: %s", gjson.MustEncodeString(req))
data := map[string]interface{}{
"platform": req.Platform, // ios或android
"package_name": req.PackageName,
"customer_active_time": fmt.Sprint(time.Now().UnixMilli()), // 毫秒时间戳,客户激活归因时间点
}
var adDatas []entity.AdvertisementOceanegine1
model := dao.AdvertisementOceanegine1.Ctx(ctx)
if req.Platform == "android" {
if req.AndroidId != "" && req.AndroidId != "__ANDROIDID__" && req.AndroidId != "00000000-0000-0000-0000-000000000000" {
_ = model.Where("adv_android_id=?", req.AndroidId).Where("os=?", 0).Scan(&adDatas)
}
if len(adDatas) == 0 {
log.Printf("AttributionHugeAmount: adData is nil %s", gjson.MustEncodeString(req))
err = errors.New("account is nil")
return
}
data["android_id"] = req.AndroidId
} else if req.Platform == "ios" {
if req.Idfa != "" && req.Idfa != "00000000-0000-0000-0000-000000000000" {
_ = model.Where("idfa=?", req.Idfa).Where("os=?", 1).Scan(&adDatas)
}
if len(adDatas) == 0 {
if req.Caid != "" {
var list []map[string]string
_ = json.Unmarshal([]byte(req.Caid), &list)
for _, v := range list {
_ = model.Where("caid=?", v["caid"]).Where("os=?", 1).Scan(&adDatas)
if len(adDatas) > 0 {
break
}
}
}
if len(adDatas) == 0 {
log.Printf("AttributionHugeAmount: adData is nil %s", gjson.MustEncodeString(req))
err = errors.New("account is nil")
return
}
}
data["idfv"] = req.Idfv
} else {
err = errors.New("platform is error " + req.Platform)
return
}
if len(adDatas) == 0 {
err = errors.New("无广告!")
return
}
var adData entity.AdvertisementOceanegine1
for _, v := range adDatas {
if adData.Id == 0 || v.LastTouchTime > adData.LastTouchTime || (v.LastTouchTime == adData.LastTouchTime && v.CDate.Unix() > adData.CDate.Unix()) {
adData = v
}
}
if adData.CallbackParam == "" {
err = errors.New("无广告!")
return
}
url := "https://analytics.oceanengine.com/sdk/app/attribution"
tmp, err := sendMsgHugeAmount(ctx, url, data)
@ -177,61 +133,22 @@ func AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.AT
if err != nil {
return
}
if resJson != nil && resJson.Get("code").Int() == 0 {
callbackUrl := resJson.Get("callback_url").String()
callbackParam := resJson.Get("callback_param").String()
if callbackParam == "" {
err = errors.New("callbackParam nil")
return
}
androidId := resJson.Get("adv_android_id").String()
//if androidId == "" {
// androidId = req.AndroidId
//}
idfa := resJson.Get("idfa").String()
//if idfa == "" {
// idfa = req.Idfa
//}
advIdfv := resJson.Get("adv_idfv").String()
//if advIdfv == "" {
// advIdfv = req.Idfv
//}
lastTouchTime := resJson.Get("last_touch_time").Int64() * 1000
if adData.LastTouchTime > lastTouchTime {
err = errors.New("lastTouchTime repeat")
return
}
advertiseData := do.AdvertisementOceanegine1{AdvAndroidId: androidId, Idfa: idfa, AdvIdfv: advIdfv,
CallbackUrl: callbackUrl, CallbackParam: callbackParam, LastTouchTime: lastTouchTime, UnitId: req.UnitId}
if req.Platform == "android" {
advertiseData.Os = 0
if androidId == "00000000-0000-0000-0000-000000000000" || androidId == "" {
err = errors.New("androidId default")
return
}
} else if req.Platform == "ios" {
advertiseData.Os = 1
if idfa == "00000000-0000-0000-0000-000000000000" || idfa == "" {
err = errors.New("idfa default")
return
}
if advIdfv == "" {
err = errors.New("adv_idfv default")
res.CallbackParam = callbackParam
res.LastTouchTime = resJson.Get("last_touch_time").Int64() * 1000
return
}
}
_, err = dao.AdvertisementOceanegine1.Ctx(ctx).WherePri(adData.Id).Update(advertiseData)
} else {
err = errors.New("获取失败")
return
}
return
}
func ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSHARes, err error) {
func ConversionHugeAmount(ctx context.Context, req *game.CSHAReq, adOc entity.AdvertisementOceanegine1) (res *game.CSHARes, err error) {
//log.Printf("ConversionHugeAmount: %s", gjson.MustEncodeString(req))
var adDatas []entity.AdvertisementOceanegine1
device := map[string]interface{}{
@ -266,18 +183,21 @@ func ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSH
err = errors.New("platform is error " + req.Platform)
return
}
if len(adDatas) == 0 {
log.Printf("ConversionHugeAmount: adData is nil %s", gjson.MustEncodeString(req))
err = errors.New("account is nil")
return
}
var adData entity.AdvertisementOceanegine1
if len(adDatas) > 0 {
for _, v := range adDatas {
if adData.Id == 0 || v.LastTouchTime > adData.LastTouchTime || (v.LastTouchTime == adData.LastTouchTime && v.CDate.Unix() > adData.CDate.Unix()) {
adData = v
}
}
if adData.Id > 0 {
adOc.Id = adData.Id
}
}
if adOc.LastTouchTime != 0 && adData.LastTouchTime < adOc.LastTouchTime {
adData = adOc
}
if adData.CallbackParam == "" {
err = errors.New("callbackParam is nil")
return
@ -325,7 +245,7 @@ func ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSH
resJson, err := gjson.DecodeToJson(tmp)
if resJson != nil && resJson.Get("code").Int() != 0 {
err = errors.New(resJson.Get("message").String())
} else if resJson.Get("code").Int() == 0 {
} else if resJson.Get("code").Int() == 0 && adData.Id > 0 {
saveData := do.AdvertisementOceanegine1{}
if req.EventType == consts.EventType_Active {
saveData.Active = 1
@ -365,21 +285,7 @@ func HugeAmount(ctx context.Context, req *game.AdvertiseHAReq) {
if req.Caid != "" {
log.Printf("HugeAmount: req: %s", gjson.MustEncodeString(req))
}
req2 := game.CSHAReq{
Platform: req.Platform,
Id: req.Id,
Idfa: req.Idfa,
EventType: req.EventType,
Properties: req.Properties,
Caid: req.Caid,
//UnitId: req.UnitId,
}
req2.Id = req.Id
_, err := ConversionHugeAmount(ctx, &req2)
if err != nil {
log.Printf("AdvertiseHugeAmount: ConversionHugeAmount err- %s, req: %s", err.Error(), gjson.MustEncodeString(req2))
return
}
req1 := game.ATHAReq{
Platform: req.Platform,
PackageName: req.PackageName,
@ -391,13 +297,26 @@ func HugeAmount(ctx context.Context, req *game.AdvertiseHAReq) {
} else if req.Platform == "ios" {
req1.Idfv = req.Id
} else {
log.Printf("AdvertiseHugeAmount: plarform err- %s, req.Platform: %s", err.Error(), req.Platform)
log.Printf("AdvertiseHugeAmount: plarform , req.Platform: %s", req.Platform)
return
}
_, err = AttributionHugeAmount(ctx, &req1)
adOc, err := AttributionHugeAmount(ctx, &req1)
if err != nil {
log.Printf("AdvertiseHugeAmount: AttributionHugeAmount err- %s, req: %s", err.Error(), gjson.MustEncodeString(req1))
return
}
req2 := game.CSHAReq{
Platform: req.Platform,
Id: req.Id,
Idfa: req.Idfa,
EventType: req.EventType,
Properties: req.Properties,
Caid: req.Caid,
}
req2.Id = req.Id
_, err = ConversionHugeAmount(ctx, &req2, adOc)
if err != nil {
log.Printf("AdvertiseHugeAmount: ConversionHugeAmount err- %s, req: %s", err.Error(), gjson.MustEncodeString(req2))
}
return
}

44
internal/serviceGame/order.go

@ -44,6 +44,9 @@ type IGameOrder interface {
ResetRechargeSign(ctx context.Context, req *game.ResetRechargeSignReq) (res *game.ResetRechargeSignRes, err error)
GetTransactionId(ctx context.Context, req *game.GetTransactionIdReq) (res *game.GetTransactionIdRes, err error)
GetHuaWeiOrderList(ctx context.Context, req *game.GetHuaWeiOrderListReq) (res *game.GetHuaWeiOrderListRes, err error)
GetHarmonyOrder(ctx context.Context, req *game.GetHarmonyOrderReq) (res *game.GetHarmonyOrderRes, err error)
GetHarmonyOrders(ctx context.Context, req *game.GetHarmonyOrdersReq) (res *game.GetHarmonyOrdersRes, err error)
GetHarmonyUserOrders(ctx context.Context, req *game.GetHarmonyUserOrdersReq) (res *game.GetHarmonyUserOrdersRes, err error)
}
type gameOrderImpl struct {
@ -598,17 +601,21 @@ func (o gameOrderImpl) AddRechargeSign(ctx context.Context, req *game.AddRecharg
}
func (o gameOrderImpl) ResetRechargeSign(ctx context.Context, req *game.ResetRechargeSignReq) (res *game.ResetRechargeSignRes, err error) {
user := service.Context().GetLoginUser(ctx)
fulluser, err1 := service.User().GetUserByUsername(ctx, user.UserName)
if err1 != nil {
return nil, err1
}
password := libUtils.EncryptPassword(req.Password, fulluser.UserSalt)
if fulluser.UserPassword != password {
log.Printf("ResetRechargeSign 密码错误! fulluser: %v, pwd: %s", gjson.MustEncodeString(fulluser), password)
return nil, errors.New("密码错误!")
}
result, err := dao.GameRechargeLevel.Ctx(ctx).Where("1=1").Delete()
//user := service.Context().GetLoginUser(ctx)
//fulluser, err1 := service.User().GetUserByUsername(ctx, user.UserName)
//if err1 != nil {
// return nil, err1
//}
//password := libUtils.EncryptPassword(req.Password, fulluser.UserSalt)
//if fulluser.UserPassword != password {
// log.Printf("ResetRechargeSign 密码错误! fulluser: %v, pwd: %s", gjson.MustEncodeString(fulluser), password)
// return nil, errors.New("密码错误!")
//}
model := dao.GameRechargeLevel.Ctx(ctx).Where("1=1")
if req.Server >= 0 {
model = model.Where("server=?", req.Server)
}
result, err := model.Delete()
if err != nil {
log.Printf("ResetRechargeSign: %v", err)
return nil, err
@ -647,3 +654,18 @@ func (o gameOrderImpl) GetHuaWeiOrderList(ctx context.Context, req *game.GetHuaW
res.Order = *order
return
}
func (o gameOrderImpl) GetHarmonyOrder(ctx context.Context, req *game.GetHarmonyOrderReq) (res *game.GetHarmonyOrderRes, err error) {
res, err = service.HarmonyOrderLookup(req)
return
}
func (o gameOrderImpl) GetHarmonyOrders(ctx context.Context, req *game.GetHarmonyOrdersReq) (res *game.GetHarmonyOrdersRes, err error) {
res, err = service.HarmonyOrders(req)
return
}
func (o gameOrderImpl) GetHarmonyUserOrders(ctx context.Context, req *game.GetHarmonyUserOrdersReq) (res *game.GetHarmonyUserOrdersRes, err error) {
res, err = service.HarmonyUserOrders(req)
return
}

6
manifest/config/IAPKey.p8

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg+oOEZ3evRCWbbgUj
BDfyrt+OinOGwgW7jcWQ27lcAWWgCgYIKoZIzj0DAQehRANCAAQy3I+h5+egmhf9
NEOdXwACHa7Brz5atHtCAaTG4OTekPW9kbAGVYR+qAmWGmI7kDQYbO+5jG30YRdJ
Ed4992DO
-----END PRIVATE KEY-----

14
manifest/config/RootCaG2Ecdsa.cer

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICGjCCAaGgAwIBAgIIShhpn519jNAwCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE
AwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDMxNjAzMDQzOVoXDTQ5MDMx
NjAzMDQzOVowUzELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEeMBwGA1UEAwwVSHVhd2VpIENCRyBSb290IENBIEcyMHYw
EAYHKoZIzj0CAQYFK4EEACIDYgAEWidkGnDSOw3/HE2y2GHl+fpWBIa5S+IlnNrs
GUvwC1I2QWvtqCHWmwFlFK95zKXiM8s9yV3VVXh7ivN8ZJO3SC5N1TCrvB2lpHMB
wcz4DA0kgHCMm/wDec6kOHx1xvCRo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUo45a9Vq8cYwqaiVyfkiS4pLcIAAwCgYIKoZI
zj0EAwMDZwAwZAIwMypeB7P0IbY7c6gpWcClhRznOJFj8uavrNu2PIoz9KIqr3jn
BlBHJs0myI7ntYpEAjBbm8eDMZY5zq5iMZUC6H7UzYSix4Uy1YlsLVV738PtKP9h
FTjgDHctXJlC5L7+ZDY=
-----END CERTIFICATE-----

5
test/build.bat

@ -0,0 +1,5 @@
$env:GOOS="windows"
go build -o ./test/copyAccount.exe ./test/test.go
go build -o ./test/rechargeAccount.exe ./test/recharge.go
go build -o ./test/rechargeAccount100.exe ./test/recharge.go

126
test/recharge.go

@ -0,0 +1,126 @@
package main
import (
"crypto/md5"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
)
const TIME_FORMAT = "2006-01-02 15:04:05"
func main() {
var unitId string
fmt.Print("unitId账号数据输入 : ")
_, err := fmt.Scan(&unitId) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
var config string
fmt.Print("输入充值配置Id(101-106) : ")
_, err = fmt.Scan(&config) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
configs := map[string]string{
"101": "6",
"102": "30",
"103": "68",
"104": "198",
"105": "328",
"106": "648",
}
if configs[config] == "" {
fmt.Println("输入错误, 请输入(101-106)的id:")
_, err = fmt.Scan(&config) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
return
}
tradeNo := fmt.Sprintf("GM%v%v", config, time.Now().UnixMilli())
body := map[string]string{"unitId": unitId, "server": "1", "channel": "000000000000", "rechargeId": config,
"tradeStatus": "SUCCESS", "externalTradeNo": tradeNo, "totalAmount": configs[config], "tradeNo": tradeNo,
"buyerId": "", "createTime": time.Now().Format(TIME_FORMAT), "payedTime": time.Now().Format(TIME_FORMAT)}
sendToServer(body)
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
fmt.Println("操作完成... (输入 'ctrl-c' 退出)")
for {
select {
case <-ticker.C:
// 这里可以添加条件检查(如读取文件、网络请求等)
default:
// 非阻塞检查用户输入(需配合 goroutine)
}
}
}
func addServerMd5(body map[string]string) string {
data := body["unitId"] + body["server"] + body["channel"] + body["rechargeId"] +
body["tradeStatus"] + body["externalTradeNo"] + body["totalAmount"] + body["tradeNo"] + body["buyerId"] + body["createTime"] +
body["payedTime"] + "b93ac9c70a071c537542f6fe95cf4d67"
sign := fmt.Sprintf("%x", md5.Sum([]byte(data)))
log.Printf("data: %s, md5: %s", data, sign)
return fmt.Sprintf("%x", md5.Sum([]byte(data)))
}
type TradeRes struct {
T string `json:"_t"`
Error int `json:"Error"`
Message string `json:"Message"`
}
func sendToServer(body map[string]string) string {
//url := "http://192.168.2.100:30300/Recharge"
url := "http://127.0.0.1:30300/Recharge"
log.Printf("notify========== url: %s", url)
body["sign"] = addServerMd5(body)
buf, err := json.Marshal(body)
if err != nil {
fmt.Println(err)
return err.Error()
}
reqBody := strings.NewReader(string(buf))
log.Printf("notify========== url: %s, reqBody: %v", url, reqBody)
reqt, _ := http.NewRequest("GET", url, reqBody)
response, err := http.DefaultClient.Do(reqt)
if err != nil {
log.Printf("notify========== server callback Error %s", err.Error())
return err.Error()
}
// 这里要格式化再输出,因为 ReadAll 返回的是字节切片
log.Printf("notify========== server callback StatusCode %d", response.StatusCode)
defer response.Body.Close()
bytes, err := io.ReadAll(response.Body)
if err != nil {
return err.Error()
}
// 这里要格式化再输出,因为 ReadAll 返回的是字节切片
log.Printf("notify========== server callback Body %s", bytes)
if response.StatusCode == 200 {
var tradeRes TradeRes
err = json.Unmarshal(bytes, &tradeRes)
if tradeRes.Error == 400000 {
return "SUCCESS"
} else {
return tradeRes.Message
}
}
return fmt.Sprint(response.StatusCode)
}

331
test/test.go

@ -11,17 +11,18 @@ import (
"log"
"os"
"os/exec"
"strconv"
"strings"
"time"
)
const redisAdress = "119.29.144.246:4114"
const redisAdress = "test.taoyuanjilogin.com:4114"
const redisPass = "peach"
const linkAccount = "mongodb://root:peach123@192.168.2.100:27017/PeachValley"
const link = "mongodb://127.0.0.1:27017/PeachValley"
const linkAccount = "mongodb://peach:peach@test.taoyuanjilogin.com:4113/PeachValley4"
const link = "mongodb://127.0.0.1:21017/PeachValley"
const notAccount = false
const hasAccount = true
func main() {
@ -47,7 +48,7 @@ func main() {
})
if model == "1" {
fmt.Printf("%s准备复制账号信息: \n", model)
fmt.Printf("%s准备下载账号信息: \n", model)
copyUnit(ctx, rdb)
} else {
fmt.Printf("%s准备替换账号信息: \n", model)
@ -56,7 +57,7 @@ func main() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
fmt.Println("程序仍在运行...(输入 'ctrl-c' 退出)")
fmt.Println("操作完成... (输入 'ctrl-c' 退出)")
for {
select {
case <-ticker.C:
@ -69,103 +70,226 @@ func main() {
func copyUnit(ctx context.Context, rdb *redis.Client) {
var destUid string
fmt.Print("输入需要复制的uid: ")
fmt.Print("输入需要下载的UniqueId: ")
_, err := fmt.Scan(&destUid) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
fmt.Printf("正在复制账号: %s\n", destUid)
int64Uid, _ := strconv.ParseInt(destUid, 10, 64)
fmt.Printf("准备下载账号: %s\n", destUid)
rdb.Do(ctx, "select", 0)
destId, err := rdb.HGet(ctx, "public_id_for_uid", fmt.Sprint(destUid)).Result()
if err != nil {
fmt.Println("public_id_for_uid: err", err)
fmt.Println("redis打开失败 无法搜索 _id, err", err)
fmt.Println("尝试通过数据库查查找账号_id。")
destFileName := "DestUnit" + destUid + ".json"
if hasAccount {
mongoexportCommand(ctx, destFileName, "Unit", int64Uid, linkAccount, 1)
} else {
mongoexportCommand(ctx, destFileName, "Unit", int64Uid, link, 1)
}
time.Sleep(5 * time.Second)
destId, _ = writeDestFile(destFileName)
if destId == "" {
fmt.Println("未找到需要下载的账号_id")
fmt.Print("请手动输入需要下载的账号_id: ")
_, err = fmt.Scan(&destId) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
}
}
if destId == "" {
fmt.Println("public_id_for_uid: value is nil")
fmt.Println("未找到需要下载的账号_id")
return
}
filename := "Unit.json"
storeFilename := "StoreComponent.json"
userDir := "./" + filename
storeDir := "./" + storeFilename
name := "mongoexport"
args := []string{}
args1 := []string{}
if notAccount {
args = append(args, fmt.Sprintf("--uri=\"%s\"", link))
args1 = append(args1, fmt.Sprintf("--uri=\"%s\"", link))
int64Id, _ := strconv.ParseInt(destId, 10, 64)
filename := "Unit" + destUid + ".json"
storeFilename := "StoreComponent" + destUid + ".json"
deadFilename := "DeadVillagerManagerComponent" + destUid + ".json"
decorationFilename := "DecorationManagerComponent" + destUid + ".json"
log.Printf("download Unit destid: %d, destUniqueId: %d", int64Id, int64Uid)
g.Try(ctx, func(ctx context.Context) {
if hasAccount {
mongoexportCommand(ctx, filename, "Unit", int64Uid, linkAccount, 1)
mongoexportCommand(ctx, storeFilename, "StoreComponent", int64Id, linkAccount, 2)
mongoexportCommand(ctx, deadFilename, "DeadVillagerManagerComponent", int64Id, linkAccount, 2)
mongoexportCommand(ctx, decorationFilename, "DecorationManagerComponent", int64Id, linkAccount, 2)
} else {
args = append(args, fmt.Sprintf("--uri=\"%s\"", linkAccount))
args = append(args, "--authenticationDatabase=admin")
args1 = append(args1, fmt.Sprintf("--uri=\"%s\"", linkAccount))
args1 = append(args1, "--authenticationDatabase=admin")
mongoexportCommand(ctx, filename, "Unit", int64Uid, link, 1)
mongoexportCommand(ctx, storeFilename, "StoreComponent", int64Id, link, 2)
mongoexportCommand(ctx, deadFilename, "DeadVillagerManagerComponent", int64Id, link, 2)
mongoexportCommand(ctx, decorationFilename, "DecorationManagerComponent", int64Id, link, 2)
}
})
args = append(args, "--collection=Unit")
}
func writeDestFile(fileName string) (destId string, err error) {
destUserDir := "./" + fileName
writeByte, err1 := os.ReadFile(destUserDir)
if err1 != nil {
return "", err1
}
var v1 map[string]interface{}
if strings.HasPrefix(string(writeByte), "[") {
user := []interface{}{}
json.Unmarshal(writeByte, &user)
v1 = user[0].(map[string]interface{})
} else {
json.Unmarshal(writeByte, &v1)
}
if _, ok := v1["_id"]; ok {
uid := v1["_id"].(map[string]interface{})
destId = fmt.Sprint(uid["$numberLong"])
}
err = os.Remove(destUserDir)
if err != nil {
log.Printf("%s os.Remove failed with %s\n", destUserDir, err)
return
}
return
}
func mongoexportCommand(ctx context.Context, filename string, collection string, uid int64, url string, qType int) {
g.Try(ctx, func(ctx context.Context) {
args := []string{}
dir := "./" + filename
args = append(args, fmt.Sprintf("--uri=\"%s\"", url))
args = append(args, fmt.Sprintf("--authenticationDatabase=admin"))
args = append(args, fmt.Sprintf("--collection=%s", collection))
args = append(args, "--jsonArray")
args = append(args, fmt.Sprintf("--out=\"%s\"", userDir))
args = append(args, fmt.Sprintf("--query=\"{\\\"UniqueId\\\":%s}\"", destUid))
args = append(args, fmt.Sprintf("--out=\"%s\"", dir))
if qType == 1 {
args = append(args, fmt.Sprintf("--query=\"{\\\"UniqueId\\\":%d}\"", uid))
} else {
args = append(args, fmt.Sprintf("--query=\"{\\\"_id\\\":%d}\"", uid))
}
args = append(args, "--jsonFormat=canonical")
args = append(args, "--type=json")
args1 = append(args1, "--collection=StoreComponent")
args1 = append(args1, "--jsonArray")
args1 = append(args1, fmt.Sprintf("--out=\"%s\"", storeDir))
args1 = append(args1, fmt.Sprintf("--query=\"{\\\"_id\\\":%s}\"", destId))
args1 = append(args1, "--jsonFormat=canonical")
args1 = append(args1, "--type=json")
cmdCommand(ctx, name, args...)
cmdCommand(ctx, name, args1...)
err := cmdCommand(ctx, "mongoexport", args...)
if err != nil {
log.Printf(" %s cmd.Run() args failed with %s\n", dir, err)
return
}
})
}
func updateUnit(ctx context.Context, rdb *redis.Client) {
var filename string
var storeFilename string
var destUid string
fmt.Print("输入需要修改的uid: ")
var srcUid string
fmt.Print("输入需要修改的UniqueId: ")
_, err := fmt.Scan(&destUid) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
fmt.Print("输入需要复制的unit文件名(需加后缀): ")
_, err = fmt.Scan(&filename) // 读取输入并赋值给变量
fmt.Print("输入需要导入的UniqueId: ")
_, err = fmt.Scan(&srcUid) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
fmt.Printf("正在复制账号: %s\n", filename)
fmt.Print("输入需要复制的store文件名(需加后缀): ")
_, err = fmt.Scan(&storeFilename) // 读取输入并赋值给变量
var filename = "Unit" + srcUid + ".json"
var storeFilename = "StoreComponent" + srcUid + ".json"
var deadFilename = "DeadVillagerManagerComponent" + srcUid + ".json"
var decorationFilename = "DecorationManagerComponent" + srcUid + ".json"
fmt.Printf("导入账号Unit信息: %s\n", filename)
fmt.Printf("导入账号store信息: %s\n", storeFilename)
fmt.Printf("导入账号deadVillager信息: %s\n", deadFilename)
fmt.Printf("导入账号decoration信息: %s\n", decorationFilename)
fmt.Printf("请按照以上命名修改需要导入的文件放到当前文件夹下 \n")
time.Sleep(2 * time.Second)
fmt.Printf("输入任意字符,按回车键继续... :")
pause1 := ""
_, err = fmt.Scan(&pause1)
if err != nil {
fmt.Println("输入错误:", err)
return
}
fmt.Printf("正在复制账号: %s\n", storeFilename)
fmt.Printf("现在修改账号: %s\n", destUid)
fmt.Printf("准备修改账号: %s\n", destUid)
int64Uid, _ := strconv.ParseInt(destUid, 10, 64)
destId, err := rdb.HGet(ctx, "public_id_for_uid", fmt.Sprint(destUid)).Result()
if err != nil {
fmt.Println("public_id_for_uid: err", err)
fmt.Println("redis无法打开 无法搜索 _id, err:", err)
fmt.Println("尝试通过数据库查查找账号_id。")
destFileName := "DestUnit" + destUid + ".json"
if hasAccount {
mongoexportCommand(ctx, destFileName, "Unit", int64Uid, linkAccount, 1)
} else {
mongoexportCommand(ctx, destFileName, "Unit", int64Uid, link, 1)
}
time.Sleep(5 * time.Second)
destId, _ = writeDestFile(destFileName)
if destId == "" {
fmt.Print("请手动输入需要修改的账号_id: ")
_, err = fmt.Scan(&destId) // 读取输入并赋值给变量
if err != nil {
fmt.Println("输入错误:", err)
return
}
}
}
if destId == "" {
fmt.Println("public_id_for_uid: value is nil")
fmt.Println("未找到需要修改的账号_id")
return
}
int64Id, _ := strconv.ParseInt(destId, 10, 64)
//intUid, _ := strconv.Atoi(destUid)
userDir := "./Unit" + destId + fmt.Sprint(time.Now().Unix()) + ".json"
srcId, err1 := writeFile1(int64Id, userDir, filename, int64Uid)
if err1 != nil {
err = err1
return
}
userDir := "./Unit" + fmt.Sprint(destId) + ".json"
srcUserDir := "./" + filename
log.Printf("copyUnit srcid: %s, destid: %d, DestName: %d", srcId, int64Id, int64Uid)
userStoreDir := "./StoreComponent" + destUid + fmt.Sprint(time.Now().Unix()) + ".json"
err = writeFile(int64Id, srcId, userStoreDir, storeFilename)
if err != nil {
return
}
userDeadDir := "./DeadVillagerManagerComponent" + destUid + fmt.Sprint(time.Now().Unix()) + ".json"
err = writeFile(int64Id, srcId, userDeadDir, deadFilename)
if err != nil {
return
}
userDecorationDir := "./DecorationManagerComponent" + destUid + fmt.Sprint(time.Now().Unix()) + ".json"
err = writeFile(int64Id, srcId, userDecorationDir, decorationFilename)
if err != nil {
return
}
g.Try(ctx, func(ctx context.Context) {
if hasAccount {
mongoimportCommand(ctx, "Unit", userDir, linkAccount)
mongoimportCommand(ctx, "StoreComponent", userStoreDir, linkAccount)
mongoimportCommand(ctx, "DeadVillagerManagerComponent", userDeadDir, linkAccount)
mongoimportCommand(ctx, "DecorationManagerComponent", userDecorationDir, linkAccount)
} else {
mongoimportCommand(ctx, "Unit", userDir, link)
mongoimportCommand(ctx, "StoreComponent", userStoreDir, link)
mongoimportCommand(ctx, "DeadVillagerManagerComponent", userDeadDir, link)
mongoimportCommand(ctx, "DecorationManagerComponent", userDecorationDir, link)
}
})
fmt.Println("CopyUnit GetRoleDelta", destId)
}
func writeFile1(destId int64, userDir string, fileName string, destName int64) (srcId string, err error) {
srcUserDir := "./" + fileName
writeByte, err1 := os.ReadFile(srcUserDir)
if err1 != nil {
return
return "", err1
}
srcUid := ""
srcId := ""
var v1 map[string]interface{}
if strings.HasPrefix(string(writeByte), "[") {
user := []interface{}{}
@ -180,83 +304,81 @@ func updateUnit(ctx context.Context, rdb *redis.Client) {
srcUid = fmt.Sprint(UniqueId["$numberInt"])
}
if _, ok := v1["_id"]; ok {
UniqueId := v1["_id"].(map[string]interface{})
srcId = fmt.Sprint(UniqueId["$numberLong"])
uid := v1["_id"].(map[string]interface{})
srcId = fmt.Sprint(uid["$numberLong"])
}
userStoreDir := "./StoreComponent" + fmt.Sprint(destUid) + ".json"
srcStoreDir := "./" + storeFilename
writeStoreByte, err1 := os.ReadFile(srcStoreDir)
if err1 != nil {
return
}
var v2 map[string]interface{}
if strings.HasPrefix(string(writeStoreByte), "[") {
user := []interface{}{}
json.Unmarshal(writeStoreByte, &user)
v2 = user[0].(map[string]interface{})
} else {
json.Unmarshal(writeStoreByte, &v2)
}
//log.Printf("copyUnit string(writeByte): %s ", string(writeByte))
log.Printf("copyUnit srcid: %s, uid: %s, destid: %s, uid: %s", srcId, srcUid, destId, destUid)
//log.Printf("copyUnit string(writeByte): %s ", gjson.MustEncodeString(user))
log.Printf("copyUnit srcid: %s, uid: %s, destId: %d, destName: %d", srcId, srcUid, destId, destName)
if srcId == "" || srcUid == "" {
return
}
writeUnitString := strings.ReplaceAll(gjson.MustEncodeString(v1), srcId, fmt.Sprint(destId))
writeStoreString := strings.ReplaceAll(gjson.MustEncodeString(v2), srcId, fmt.Sprint(destId))
//log.Printf("copyUnit writeString: %s,", writeString)
writeUnitString = ReplaceLastOccurrence(writeUnitString, srcUid, fmt.Sprint(destUid))
//log.Printf("copyUnit writeString: %s,", writeString)
writeUnitString = ReplaceLastOccurrence(writeUnitString, srcUid, fmt.Sprint(destName))
if err = os.WriteFile(userDir, []byte(writeUnitString), 0666); err != nil {
log.Println(err)
return
}
if err = os.WriteFile(userStoreDir, []byte(writeStoreString), 0666); err != nil {
log.Println(err)
return
}
func writeFile(destId int64, srcId string, userDir string, fileName string) (err error) {
srcDir := "./" + fileName
writeByte, err1 := os.ReadFile(srcDir)
if err1 != nil {
return err1
}
name := "mongoimport"
args := []string{}
args1 := []string{}
if notAccount {
args = append(args, fmt.Sprintf("--uri=\"%s\"", link))
args1 = append(args1, fmt.Sprintf("--uri=\"%s\"", link))
var v map[string]interface{}
if strings.HasPrefix(string(writeByte), "[") {
user := []interface{}{}
json.Unmarshal(writeByte, &user)
if len(user) != 0 {
v = user[0].(map[string]interface{})
}
} else {
args = append(args, fmt.Sprintf("--uri=\"%s\"", linkAccount))
args = append(args, "--authenticationDatabase=admin")
args1 = append(args1, fmt.Sprintf("--uri=\"%s\"", linkAccount))
args1 = append(args1, "--authenticationDatabase=admin")
json.Unmarshal(writeByte, &v)
}
if len(v) != 0 {
writeString := strings.ReplaceAll(gjson.MustEncodeString(v), srcId, fmt.Sprint(destId))
if err = os.WriteFile(userDir, []byte(writeString), 0666); err != nil {
log.Println(err)
return
}
}
args = append(args, "--collection=Unit")
args = append(args, fmt.Sprintf("--file=\"%s\"", userDir))
return
}
func mongoimportCommand(ctx context.Context, collection, dir, url string) {
args := []string{}
args = append(args, fmt.Sprintf("--uri=\"%s\"", url))
args = append(args, fmt.Sprintf("--authenticationDatabase=admin"))
args = append(args, fmt.Sprintf("--collection=%s", collection))
args = append(args, fmt.Sprintf("--file=\"%s\"", dir))
args = append(args, "--type=json")
args = append(args, "--mode=upsert")
args1 = append(args1, "--collection=StoreComponent")
args1 = append(args1, fmt.Sprintf("--file=\"%s\"", userStoreDir))
args1 = append(args1, "--type=json")
args1 = append(args1, "--mode=upsert")
cmdCommand(ctx, name, args...)
cmdCommand(ctx, name, args1...)
cmdCommand(ctx, "del", userDir)
cmdCommand(ctx, "del", userStoreDir)
err := cmdCommand(ctx, "mongoimport", args...)
if err != nil {
log.Printf("%s cmd.Run() failed with %s\n", dir, err)
return
}
fmt.Println("CopyUnit GetRoleDelta", destId)
err = os.Remove(dir)
if err != nil {
log.Printf("%s os.Remove failed with %s\n", dir, err)
return
}
}
func cmdCommand(ctx context.Context, name string, args ...string) {
g.Try(ctx, func(ctx context.Context) {
func cmdCommand(ctx context.Context, name string, args ...string) (err error) {
err = g.Try(ctx, func(ctx context.Context) {
cmd := exec.Command(name, args...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout // 标准输出
cmd.Stderr = &stderr // 标准错误
err := cmd.Run()
err = cmd.Run()
if err != nil {
log.Printf("cmd.Run() failed with %s\n", err)
return
@ -264,6 +386,7 @@ func cmdCommand(ctx context.Context, name string, args ...string) {
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
fmt.Printf("out:\n%s\nerr:\n%s\n", outStr, errStr)
})
return
}
// ReplaceLastOccurrence 替换最后一个匹配的子字符串

Loading…
Cancel
Save