diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.abi new file mode 100644 index 000000000..f9d8f7663 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"evidence","type":"string"},{"indexed":false,"internalType":"string","name":"nodeID","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"DecisionRecorded","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"decisions","outputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"approved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getDecision","outputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"approved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDecisionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"recordDecision","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"evidence","type":"string"}],"name":"verifyEvidence","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.bin new file mode 100644 index 000000000..64acb8752 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610b7e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636f13f95a1461005c578063af7548481461008c578063b829df82146100bf578063b8c0dbbd146100f2578063e4ff19da14610122575b600080fd5b610076600480360381019061007191906107ae565b610140565b6040516100839190610812565b60405180910390f35b6100a660048036038101906100a19190610863565b61014b565b6040516100b69493929190610927565b60405180910390f35b6100d960048036038101906100d49190610863565b610335565b6040516100e99493929190610927565b60405180910390f35b61010c600480360381019061010791906109a6565b610492565b6040516101199190610812565b60405180910390f35b61012a6105a5565b6040516101379190610a31565b60405180910390f35b600060019050919050565b6060806000806000805490508510610198576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018f90610a98565b60405180910390fd5b60008086815481106101ad576101ac610ab8565b5b90600052602060002090600402016040518060800160405290816000820180546101d690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461020290610b16565b801561024f5780601f106102245761010080835404028352916020019161024f565b820191906000526020600020905b81548152906001019060200180831161023257829003601f168201915b5050505050815260200160018201805461026890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461029490610b16565b80156102e15780601f106102b6576101008083540402835291602001916102e1565b820191906000526020600020905b8154815290600101906020018083116102c457829003601f168201915b50505050508152602001600282015481526020016003820160009054906101000a900460ff161515151581525050905080600001518160200151826040015183606001519450945094509450509193509193565b6000818154811061034557600080fd5b906000526020600020906004020160009150905080600001805461036890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461039490610b16565b80156103e15780601f106103b6576101008083540402835291602001916103e1565b820191906000526020600020905b8154815290600101906020018083116103c457829003601f168201915b5050505050908060010180546103f690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461042290610b16565b801561046f5780601f106104445761010080835404028352916020019161046f565b820191906000526020600020905b81548152906001019060200180831161045257829003601f168201915b5050505050908060020154908060030160009054906101000a900460ff16905084565b60008060405180608001604052808681526020018581526020014281526020018415158152509050600081908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000190805190602001906105029291906105b1565b50602082015181600101908051906020019061051f9291906105b1565b506040820151816002015560608201518160030160006101000a81548160ff02191690831515021790555050507f641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb08542881600001518260200151836040015184606001516040516105919493929190610927565b60405180910390a160019150509392505050565b60008080549050905090565b8280546105bd90610b16565b90600052602060002090601f0160209004810192826105df5760008555610626565b82601f106105f857805160ff1916838001178555610626565b82800160010185558215610626579182015b8281111561062557825182559160200191906001019061060a565b5b5090506106339190610637565b5090565b5b80821115610650576000816000905550600101610638565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6106bb82610672565b810181811067ffffffffffffffff821117156106da576106d9610683565b5b80604052505050565b60006106ed610654565b90506106f982826106b2565b919050565b600067ffffffffffffffff82111561071957610718610683565b5b61072282610672565b9050602081019050919050565b82818337600083830152505050565b600061075161074c846106fe565b6106e3565b90508281526020810184848401111561076d5761076c61066d565b5b61077884828561072f565b509392505050565b600082601f83011261079557610794610668565b5b81356107a584826020860161073e565b91505092915050565b6000602082840312156107c4576107c361065e565b5b600082013567ffffffffffffffff8111156107e2576107e1610663565b5b6107ee84828501610780565b91505092915050565b60008115159050919050565b61080c816107f7565b82525050565b60006020820190506108276000830184610803565b92915050565b6000819050919050565b6108408161082d565b811461084b57600080fd5b50565b60008135905061085d81610837565b92915050565b6000602082840312156108795761087861065e565b5b60006108878482850161084e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156108ca5780820151818401526020810190506108af565b838111156108d9576000848401525b50505050565b60006108ea82610890565b6108f4818561089b565b93506109048185602086016108ac565b61090d81610672565b840191505092915050565b6109218161082d565b82525050565b6000608082019050818103600083015261094181876108df565b9050818103602083015261095581866108df565b90506109646040830185610918565b6109716060830184610803565b95945050505050565b610983816107f7565b811461098e57600080fd5b50565b6000813590506109a08161097a565b92915050565b6000806000606084860312156109bf576109be61065e565b5b600084013567ffffffffffffffff8111156109dd576109dc610663565b5b6109e986828701610780565b935050602084013567ffffffffffffffff811115610a0a57610a09610663565b5b610a1686828701610780565b9250506040610a2786828701610991565b9150509250925092565b6000602082019050610a466000830184610918565b92915050565b7f496e646578206f7574206f6620626f756e647300000000000000000000000000600082015250565b6000610a8260138361089b565b9150610a8d82610a4c565b602082019050919050565b60006020820190508181036000830152610ab181610a75565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610b2e57607f821691505b60208210811415610b4257610b41610ae7565b5b5091905056fea2646970667358221220977b96c748e0423ec306543f50d16911b1ba1f1f2928bff91954f64010dea1d264736f6c634300080b0033 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.go new file mode 100644 index 000000000..e28063252 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.go @@ -0,0 +1,445 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package Decision + +import ( + "fmt" + "math/big" + "strings" + + "github.com/FISCO-BCOS/go-sdk/v3/abi" + "github.com/FISCO-BCOS/go-sdk/v3/abi/bind" + "github.com/FISCO-BCOS/go-sdk/v3/types" + "github.com/ethereum/go-ethereum/common" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = abi.U256 + _ = bind.Bind + _ = common.Big1 +) + +// DecisionStorageABI is the input ABI used to generate the binding from. +const DecisionStorageABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"DecisionRecorded\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"decisions\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getDecision\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDecisionCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"recordDecision\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"}],\"name\":\"verifyEvidence\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" + +// DecisionStorageBin is the compiled bytecode used for deploying new contracts. +var DecisionStorageBin = "0x608060405234801561001057600080fd5b50610b7e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636f13f95a1461005c578063af7548481461008c578063b829df82146100bf578063b8c0dbbd146100f2578063e4ff19da14610122575b600080fd5b610076600480360381019061007191906107ae565b610140565b6040516100839190610812565b60405180910390f35b6100a660048036038101906100a19190610863565b61014b565b6040516100b69493929190610927565b60405180910390f35b6100d960048036038101906100d49190610863565b610335565b6040516100e99493929190610927565b60405180910390f35b61010c600480360381019061010791906109a6565b610492565b6040516101199190610812565b60405180910390f35b61012a6105a5565b6040516101379190610a31565b60405180910390f35b600060019050919050565b6060806000806000805490508510610198576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018f90610a98565b60405180910390fd5b60008086815481106101ad576101ac610ab8565b5b90600052602060002090600402016040518060800160405290816000820180546101d690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461020290610b16565b801561024f5780601f106102245761010080835404028352916020019161024f565b820191906000526020600020905b81548152906001019060200180831161023257829003601f168201915b5050505050815260200160018201805461026890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461029490610b16565b80156102e15780601f106102b6576101008083540402835291602001916102e1565b820191906000526020600020905b8154815290600101906020018083116102c457829003601f168201915b50505050508152602001600282015481526020016003820160009054906101000a900460ff161515151581525050905080600001518160200151826040015183606001519450945094509450509193509193565b6000818154811061034557600080fd5b906000526020600020906004020160009150905080600001805461036890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461039490610b16565b80156103e15780601f106103b6576101008083540402835291602001916103e1565b820191906000526020600020905b8154815290600101906020018083116103c457829003601f168201915b5050505050908060010180546103f690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461042290610b16565b801561046f5780601f106104445761010080835404028352916020019161046f565b820191906000526020600020905b81548152906001019060200180831161045257829003601f168201915b5050505050908060020154908060030160009054906101000a900460ff16905084565b60008060405180608001604052808681526020018581526020014281526020018415158152509050600081908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000190805190602001906105029291906105b1565b50602082015181600101908051906020019061051f9291906105b1565b506040820151816002015560608201518160030160006101000a81548160ff02191690831515021790555050507f641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb08542881600001518260200151836040015184606001516040516105919493929190610927565b60405180910390a160019150509392505050565b60008080549050905090565b8280546105bd90610b16565b90600052602060002090601f0160209004810192826105df5760008555610626565b82601f106105f857805160ff1916838001178555610626565b82800160010185558215610626579182015b8281111561062557825182559160200191906001019061060a565b5b5090506106339190610637565b5090565b5b80821115610650576000816000905550600101610638565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6106bb82610672565b810181811067ffffffffffffffff821117156106da576106d9610683565b5b80604052505050565b60006106ed610654565b90506106f982826106b2565b919050565b600067ffffffffffffffff82111561071957610718610683565b5b61072282610672565b9050602081019050919050565b82818337600083830152505050565b600061075161074c846106fe565b6106e3565b90508281526020810184848401111561076d5761076c61066d565b5b61077884828561072f565b509392505050565b600082601f83011261079557610794610668565b5b81356107a584826020860161073e565b91505092915050565b6000602082840312156107c4576107c361065e565b5b600082013567ffffffffffffffff8111156107e2576107e1610663565b5b6107ee84828501610780565b91505092915050565b60008115159050919050565b61080c816107f7565b82525050565b60006020820190506108276000830184610803565b92915050565b6000819050919050565b6108408161082d565b811461084b57600080fd5b50565b60008135905061085d81610837565b92915050565b6000602082840312156108795761087861065e565b5b60006108878482850161084e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156108ca5780820151818401526020810190506108af565b838111156108d9576000848401525b50505050565b60006108ea82610890565b6108f4818561089b565b93506109048185602086016108ac565b61090d81610672565b840191505092915050565b6109218161082d565b82525050565b6000608082019050818103600083015261094181876108df565b9050818103602083015261095581866108df565b90506109646040830185610918565b6109716060830184610803565b95945050505050565b610983816107f7565b811461098e57600080fd5b50565b6000813590506109a08161097a565b92915050565b6000806000606084860312156109bf576109be61065e565b5b600084013567ffffffffffffffff8111156109dd576109dc610663565b5b6109e986828701610780565b935050602084013567ffffffffffffffff811115610a0a57610a09610663565b5b610a1686828701610780565b9250506040610a2786828701610991565b9150509250925092565b6000602082019050610a466000830184610918565b92915050565b7f496e646578206f7574206f6620626f756e647300000000000000000000000000600082015250565b6000610a8260138361089b565b9150610a8d82610a4c565b602082019050919050565b60006020820190508181036000830152610ab181610a75565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610b2e57607f821691505b60208210811415610b4257610b41610ae7565b5b5091905056fea2646970667358221220977b96c748e0423ec306543f50d16911b1ba1f1f2928bff91954f64010dea1d264736f6c634300080b0033" +var DecisionStorageSMBin = "0x" + +// DeployDecisionStorage deploys a new contract, binding an instance of DecisionStorage to it. +func DeployDecisionStorage(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Receipt, *DecisionStorage, error) { + parsed, err := abi.JSON(strings.NewReader(DecisionStorageABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(DecisionStorageSMBin) + } else { + bytecode = common.FromHex(DecisionStorageBin) + } + if len(bytecode) == 0 { + return common.Address{}, nil, nil, fmt.Errorf("cannot deploy empty bytecode") + } + address, receipt, contract, err := bind.DeployContract(auth, parsed, bytecode, DecisionStorageABI, backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, receipt, &DecisionStorage{DecisionStorageCaller: DecisionStorageCaller{contract: contract}, DecisionStorageTransactor: DecisionStorageTransactor{contract: contract}, DecisionStorageFilterer: DecisionStorageFilterer{contract: contract}}, nil +} + +func AsyncDeployDecisionStorage(auth *bind.TransactOpts, handler func(*types.Receipt, error), backend bind.ContractBackend) (*types.Transaction, error) { + parsed, err := abi.JSON(strings.NewReader(DecisionStorageABI)) + if err != nil { + return nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(DecisionStorageSMBin) + } else { + bytecode = common.FromHex(DecisionStorageBin) + } + if len(bytecode) == 0 { + return nil, fmt.Errorf("cannot deploy empty bytecode") + } + tx, err := bind.AsyncDeployContract(auth, handler, parsed, bytecode, DecisionStorageABI, backend) + if err != nil { + return nil, err + } + return tx, nil +} + +// DecisionStorage is an auto generated Go binding around a Solidity contract. +type DecisionStorage struct { + DecisionStorageCaller // Read-only binding to the contract + DecisionStorageTransactor // Write-only binding to the contract + DecisionStorageFilterer // Log filterer for contract events +} + +// DecisionStorageCaller is an auto generated read-only Go binding around a Solidity contract. +type DecisionStorageCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DecisionStorageTransactor is an auto generated write-only Go binding around a Solidity contract. +type DecisionStorageTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DecisionStorageFilterer is an auto generated log filtering Go binding around a Solidity contract events. +type DecisionStorageFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DecisionStorageSession is an auto generated Go binding around a Solidity contract, +// with pre-set call and transact options. +type DecisionStorageSession struct { + Contract *DecisionStorage // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DecisionStorageCallerSession is an auto generated read-only Go binding around a Solidity contract, +// with pre-set call options. +type DecisionStorageCallerSession struct { + Contract *DecisionStorageCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// DecisionStorageTransactorSession is an auto generated write-only Go binding around a Solidity contract, +// with pre-set transact options. +type DecisionStorageTransactorSession struct { + Contract *DecisionStorageTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DecisionStorageRaw is an auto generated low-level Go binding around a Solidity contract. +type DecisionStorageRaw struct { + Contract *DecisionStorage // Generic contract binding to access the raw methods on +} + +// DecisionStorageCallerRaw is an auto generated low-level read-only Go binding around a Solidity contract. +type DecisionStorageCallerRaw struct { + Contract *DecisionStorageCaller // Generic read-only contract binding to access the raw methods on +} + +// DecisionStorageTransactorRaw is an auto generated low-level write-only Go binding around a Solidity contract. +type DecisionStorageTransactorRaw struct { + Contract *DecisionStorageTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewDecisionStorage creates a new instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorage(address common.Address, backend bind.ContractBackend) (*DecisionStorage, error) { + contract, err := bindDecisionStorage(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DecisionStorage{DecisionStorageCaller: DecisionStorageCaller{contract: contract}, DecisionStorageTransactor: DecisionStorageTransactor{contract: contract}, DecisionStorageFilterer: DecisionStorageFilterer{contract: contract}}, nil +} + +// NewDecisionStorageCaller creates a new read-only instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorageCaller(address common.Address, caller bind.ContractCaller) (*DecisionStorageCaller, error) { + contract, err := bindDecisionStorage(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DecisionStorageCaller{contract: contract}, nil +} + +// NewDecisionStorageTransactor creates a new write-only instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorageTransactor(address common.Address, transactor bind.ContractTransactor) (*DecisionStorageTransactor, error) { + contract, err := bindDecisionStorage(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DecisionStorageTransactor{contract: contract}, nil +} + +// NewDecisionStorageFilterer creates a new log filterer instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorageFilterer(address common.Address, filterer bind.ContractFilterer) (*DecisionStorageFilterer, error) { + contract, err := bindDecisionStorage(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DecisionStorageFilterer{contract: contract}, nil +} + +// bindDecisionStorage binds a generic wrapper to an already deployed contract. +func bindDecisionStorage(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(DecisionStorageABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_DecisionStorage *DecisionStorageRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _DecisionStorage.Contract.DecisionStorageCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_DecisionStorage *DecisionStorageRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.DecisionStorageTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_DecisionStorage *DecisionStorageRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.DecisionStorageTransactor.contract.TransactWithResult(opts, result, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_DecisionStorage *DecisionStorageCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _DecisionStorage.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_DecisionStorage *DecisionStorageTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_DecisionStorage *DecisionStorageTransactorRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.contract.TransactWithResult(opts, result, method, params...) +} + +// Decisions is a free data retrieval call binding the contract method 0xb829df82. +// +// Solidity: function decisions(uint256 ) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCaller) Decisions(opts *bind.CallOpts, arg0 *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + ret := new(struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool + }) + out := ret + err := _DecisionStorage.contract.Call(opts, out, "decisions", arg0) + return *ret, err +} + +// Decisions is a free data retrieval call binding the contract method 0xb829df82. +// +// Solidity: function decisions(uint256 ) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) Decisions(arg0 *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.Decisions(&_DecisionStorage.CallOpts, arg0) +} + +// Decisions is a free data retrieval call binding the contract method 0xb829df82. +// +// Solidity: function decisions(uint256 ) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCallerSession) Decisions(arg0 *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.Decisions(&_DecisionStorage.CallOpts, arg0) +} + +// GetDecision is a free data retrieval call binding the contract method 0xaf754848. +// +// Solidity: function getDecision(uint256 index) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCaller) GetDecision(opts *bind.CallOpts, index *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + ret := new(struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool + }) + out := ret + err := _DecisionStorage.contract.Call(opts, out, "getDecision", index) + return *ret, err +} + +// GetDecision is a free data retrieval call binding the contract method 0xaf754848. +// +// Solidity: function getDecision(uint256 index) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) GetDecision(index *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.GetDecision(&_DecisionStorage.CallOpts, index) +} + +// GetDecision is a free data retrieval call binding the contract method 0xaf754848. +// +// Solidity: function getDecision(uint256 index) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCallerSession) GetDecision(index *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.GetDecision(&_DecisionStorage.CallOpts, index) +} + +// GetDecisionCount is a free data retrieval call binding the contract method 0xe4ff19da. +// +// Solidity: function getDecisionCount() constant returns(uint256) +func (_DecisionStorage *DecisionStorageCaller) GetDecisionCount(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _DecisionStorage.contract.Call(opts, out, "getDecisionCount") + return *ret0, err +} + +// GetDecisionCount is a free data retrieval call binding the contract method 0xe4ff19da. +// +// Solidity: function getDecisionCount() constant returns(uint256) +func (_DecisionStorage *DecisionStorageSession) GetDecisionCount() (*big.Int, error) { + return _DecisionStorage.Contract.GetDecisionCount(&_DecisionStorage.CallOpts) +} + +// GetDecisionCount is a free data retrieval call binding the contract method 0xe4ff19da. +// +// Solidity: function getDecisionCount() constant returns(uint256) +func (_DecisionStorage *DecisionStorageCallerSession) GetDecisionCount() (*big.Int, error) { + return _DecisionStorage.Contract.GetDecisionCount(&_DecisionStorage.CallOpts) +} + +// VerifyEvidence is a free data retrieval call binding the contract method 0x6f13f95a. +// +// Solidity: function verifyEvidence(string evidence) constant returns(bool) +func (_DecisionStorage *DecisionStorageCaller) VerifyEvidence(opts *bind.CallOpts, evidence string) (bool, error) { + var ( + ret0 = new(bool) + ) + out := ret0 + err := _DecisionStorage.contract.Call(opts, out, "verifyEvidence", evidence) + return *ret0, err +} + +// VerifyEvidence is a free data retrieval call binding the contract method 0x6f13f95a. +// +// Solidity: function verifyEvidence(string evidence) constant returns(bool) +func (_DecisionStorage *DecisionStorageSession) VerifyEvidence(evidence string) (bool, error) { + return _DecisionStorage.Contract.VerifyEvidence(&_DecisionStorage.CallOpts, evidence) +} + +// VerifyEvidence is a free data retrieval call binding the contract method 0x6f13f95a. +// +// Solidity: function verifyEvidence(string evidence) constant returns(bool) +func (_DecisionStorage *DecisionStorageCallerSession) VerifyEvidence(evidence string) (bool, error) { + return _DecisionStorage.Contract.VerifyEvidence(&_DecisionStorage.CallOpts, evidence) +} + +// RecordDecision is a paid mutator transaction binding the contract method 0xb8c0dbbd. +// +// Solidity: function recordDecision(string evidence, string nodeID, bool approved) returns(bool) +func (_DecisionStorage *DecisionStorageTransactor) RecordDecision(opts *bind.TransactOpts, evidence string, nodeID string, approved bool) (bool, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(bool) + ) + out := ret0 + transaction, receipt, err := _DecisionStorage.contract.TransactWithResult(opts, out, "recordDecision", evidence, nodeID, approved) + return *ret0, transaction, receipt, err +} + +func (_DecisionStorage *DecisionStorageTransactor) AsyncRecordDecision(handler func(*types.Receipt, error), opts *bind.TransactOpts, evidence string, nodeID string, approved bool) (*types.Transaction, error) { + return _DecisionStorage.contract.AsyncTransact(opts, handler, "recordDecision", evidence, nodeID, approved) +} + +// RecordDecision is a paid mutator transaction binding the contract method 0xb8c0dbbd. +// +// Solidity: function recordDecision(string evidence, string nodeID, bool approved) returns(bool) +func (_DecisionStorage *DecisionStorageSession) RecordDecision(evidence string, nodeID string, approved bool) (bool, *types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.RecordDecision(&_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +func (_DecisionStorage *DecisionStorageSession) AsyncRecordDecision(handler func(*types.Receipt, error), evidence string, nodeID string, approved bool) (*types.Transaction, error) { + return _DecisionStorage.Contract.AsyncRecordDecision(handler, &_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +// RecordDecision is a paid mutator transaction binding the contract method 0xb8c0dbbd. +// +// Solidity: function recordDecision(string evidence, string nodeID, bool approved) returns(bool) +func (_DecisionStorage *DecisionStorageTransactorSession) RecordDecision(evidence string, nodeID string, approved bool) (bool, *types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.RecordDecision(&_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +func (_DecisionStorage *DecisionStorageTransactorSession) AsyncRecordDecision(handler func(*types.Receipt, error), evidence string, nodeID string, approved bool) (*types.Transaction, error) { + return _DecisionStorage.Contract.AsyncRecordDecision(handler, &_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +// DecisionStorageDecisionRecorded represents a DecisionRecorded event raised by the DecisionStorage contract. +type DecisionStorageDecisionRecorded struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool + Raw types.Log // Blockchain specific contextual infos +} + +// WatchDecisionRecorded is a free log subscription operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageFilterer) WatchDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.contract.WatchLogs(fromBlock, handler, "DecisionRecorded") +} + +func (_DecisionStorage *DecisionStorageFilterer) WatchAllDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.contract.WatchLogs(fromBlock, handler, "DecisionRecorded") +} + +// ParseDecisionRecorded is a log parse operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageFilterer) ParseDecisionRecorded(log types.Log) (*DecisionStorageDecisionRecorded, error) { + event := new(DecisionStorageDecisionRecorded) + if err := _DecisionStorage.contract.UnpackLog(event, "DecisionRecorded", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchDecisionRecorded is a free log subscription operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) WatchDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.Contract.WatchDecisionRecorded(fromBlock, handler) +} + +func (_DecisionStorage *DecisionStorageSession) WatchAllDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.Contract.WatchAllDecisionRecorded(fromBlock, handler) +} + +// ParseDecisionRecorded is a log parse operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) ParseDecisionRecorded(log types.Log) (*DecisionStorageDecisionRecorded, error) { + return _DecisionStorage.Contract.ParseDecisionRecorded(log) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.sol new file mode 100644 index 000000000..19c90c817 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/Decision/DecisionStorage.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.10 <0.8.20; + +contract DecisionStorage { + // 决策记录结构 + struct Decision { + string evidence; // 证据 + string nodeID; // 节点ID + uint256 timestamp; // 时间戳 + bool approved; // 是否批准 + } + + // 存储所有决策记录 + Decision[] public decisions; + + // 记录决策的事件 + event DecisionRecorded( + string evidence, + string nodeID, + uint256 timestamp, + bool approved + ); + + function recordDecision(string memory evidence, string memory nodeID, bool approved) public returns (bool) { + Decision memory newDecision = Decision({ + evidence: evidence, + nodeID: nodeID, + timestamp: block.timestamp, + approved: approved + }); + + // 存储决策 + decisions.push(newDecision); + + // 触发事件 + emit DecisionRecorded( + newDecision.evidence, + newDecision.nodeID, + newDecision.timestamp, + newDecision.approved + ); + + return true; + } + + // 验证证据 可以添加具体的验证逻辑 + function verifyEvidence(string memory evidence) public view returns (bool) { + // 目前返回true用于测试 + return true; + } + + // 获取决策记录数量 + function getDecisionCount() public view returns (uint256) { + return decisions.length; + } + + // 获取指定索引的决策记录 + function getDecision(uint256 index) public view returns ( + string memory evidence, + string memory nodeID, + uint256 timestamp, + bool approved + ) { + require(index < decisions.length, "Index out of bounds"); + Decision memory decision = decisions[index]; + return ( + decision.evidence, + decision.nodeID, + decision.timestamp, + decision.approved + ); + } + +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KVTable.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KVTable.abi new file mode 100644 index 000000000..134580fc6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KVTable.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"get","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"set","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KVTable.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KVTable.bin new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.abi new file mode 100644 index 000000000..1103d5e2d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"CreateResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"InsertResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"RemoveResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"UpdateResult","type":"event"},{"inputs":[{"internalType":"string","name":"tableName","type":"string"},{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"name":"createTable","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"desc","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"secret","type":"string"}],"name":"insert","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"}],"name":"remove","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"}],"name":"select","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"secret","type":"string"}],"name":"update","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.bin new file mode 100644 index 000000000..55e893525 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b506000600267ffffffffffffffff81111562000032576200003162000399565b5b6040519080825280602002602001820160405280156200006757816020015b6060815260200190600190039081620000515790505b5090506040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525081600081518110620000b757620000b6620003c8565b5b60200260200101819052506040518060400160405280600681526020017f7365637265740000000000000000000000000000000000000000000000000000815250816001815181106200010f576200010e620003c8565b5b6020026020010181905250600060405180604001604052806040518060400160405280600281526020017f6964000000000000000000000000000000000000000000000000000000000000815250815260200183815250905061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e6040518060400160405280600b81526020017f745f6b65795f7368617265000000000000000000000000000000000000000000815250836040518363ffffffff1660e01b8152600401620001dc92919062000605565b6020604051808303816000875af1158015620001fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000222919062000683565b50600061100273ffffffffffffffffffffffffffffffffffffffff1663f23f63c96040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b8152600401620002979190620006b5565b602060405180830381865afa158015620002b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002db91906200073e565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000347906200079a565b60405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050620007bc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081519050919050565b600082825260208201905092915050565b60005b838110156200043357808201518184015260208101905062000416565b8381111562000443576000848401525b50505050565b6000601f19601f8301169050919050565b60006200046782620003f7565b62000473818562000402565b93506200048581856020860162000413565b620004908162000449565b840191505092915050565b600082825260208201905092915050565b6000620004b982620003f7565b620004c581856200049b565b9350620004d781856020860162000413565b620004e28162000449565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000620005278383620004ac565b905092915050565b6000602082019050919050565b60006200054982620004ed565b620005558185620004f8565b935083602082028501620005698562000509565b8060005b85811015620005ab578484038952815162000589858262000519565b945062000596836200052f565b925060208a019950506001810190506200056d565b50829750879550505050505092915050565b60006040830160008301518482036000860152620005dc8282620004ac565b91505060208301518482036020860152620005f882826200053c565b9150508091505092915050565b600060408201905081810360008301526200062181856200045a565b90508181036020830152620006378184620005bd565b90509392505050565b600080fd5b60008160030b9050919050565b6200065d8162000645565b81146200066957600080fd5b50565b6000815190506200067d8162000652565b92915050565b6000602082840312156200069c576200069b62000640565b5b6000620006ac848285016200066c565b91505092915050565b60006020820190508181036000830152620006d181846200045a565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200070682620006d9565b9050919050565b6200071881620006f9565b81146200072457600080fd5b50565b60008151905062000738816200070d565b92915050565b60006020828403121562000757576200075662000640565b5b6000620007678482850162000727565b91505092915050565b50565b60006200078260008362000402565b91506200078f8262000770565b600082019050919050565b60006020820190508181036000830152620007b58162000773565b9050919050565b6115c480620007cc6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80632fe99bdc1461006757806331c3e4561461009757806355f150f1146100c75780636a5bae4e146100e657806380599e4b14610116578063fcd7e3c114610146575b600080fd5b610081600480360381019061007c9190610a2f565b610177565b60405161008e9190610af2565b60405180910390f35b6100b160048036038101906100ac9190610a2f565b610309565b6040516100be9190610af2565b60405180910390f35b6100cf61051b565b6040516100dd929190610ca1565b60405180910390f35b61010060048036038101906100fb9190610dbe565b6105eb565b60405161010d9190610e7e565b60405180910390f35b610130600480360381019061012b9190610e99565b6106cf565b60405161013d9190610af2565b60405180910390f35b610160600480360381019061015b9190610e99565b6107b0565b60405161016e929190610ee2565b60405180910390f35b600080600267ffffffffffffffff81111561019557610194610904565b5b6040519080825280602002602001820160405280156101c857816020015b60608152602001906001900390816101b35790505b50905083816000815181106101e0576101df610f19565b5b60200260200101819052508281600181518110610200576101ff610f19565b5b60200260200101819052506000604051806040016040528087815260200183815250905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635c6e105f836040518263ffffffff1660e01b81526004016102809190611012565b6020604051808303816000875af115801561029f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c39190611060565b90507fc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce816040516102f491906110c8565b60405180910390a18093505050509392505050565b600080600267ffffffffffffffff81111561032757610326610904565b5b60405190808252806020026020018201604052801561036057816020015b61034d6108bb565b8152602001906001900390816103455790505b50905060405180604001604052806040518060400160405280600481526020017f6e616d6500000000000000000000000000000000000000000000000000000000815250815260200185815250816000815181106103c1576103c0610f19565b5b602002602001018190525060405180604001604052806040518060400160405280600681526020017f73656372657400000000000000000000000000000000000000000000000000008152508152602001848152508160018151811061042a57610429610f19565b5b602002602001018190525060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166341ffd75f87846040518363ffffffff1660e01b81526004016104939291906111e9565b6020604051808303816000875af11580156104b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d69190611060565b90507f8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a8160405161050791906110c8565b60405180910390a180925050509392505050565b606080600061100273ffffffffffffffffffffffffffffffffffffffff16635d0d6d546040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016105909190611220565b600060405180830381865afa1580156105ad573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906105d691906113f9565b90508060000151816020015192509250509091565b6000806040518060400160405280858152602001848152509050600061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e87846040518363ffffffff1660e01b8152600401610644929190611486565b6020604051808303816000875af1158015610663573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106879190611060565b90507fb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210816040516106b891906110c8565b60405180910390a18060030b925050509392505050565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166380599e4b846040518263ffffffff1660e01b815260040161072b9190611220565b6020604051808303816000875af115801561074a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076e9190611060565b90507f4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc31573581128160405161079f91906110c8565b60405180910390a180915050919050565b60608060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fcd7e3c1856040518263ffffffff1660e01b815260040161080f9190611220565b600060405180830381865afa15801561082c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906108559190611545565b9050606080600283602001515114156108ad5782602001516000815181106108805761087f610f19565b5b6020026020010151915082602001516001815181106108a2576108a1610f19565b5b602002602001015190505b818194509450505050915091565b604051806040016040528060608152602001606081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61093c826108f3565b810181811067ffffffffffffffff8211171561095b5761095a610904565b5b80604052505050565b600061096e6108d5565b905061097a8282610933565b919050565b600067ffffffffffffffff82111561099a57610999610904565b5b6109a3826108f3565b9050602081019050919050565b82818337600083830152505050565b60006109d26109cd8461097f565b610964565b9050828152602081018484840111156109ee576109ed6108ee565b5b6109f98482856109b0565b509392505050565b600082601f830112610a1657610a156108e9565b5b8135610a268482602086016109bf565b91505092915050565b600080600060608486031215610a4857610a476108df565b5b600084013567ffffffffffffffff811115610a6657610a656108e4565b5b610a7286828701610a01565b935050602084013567ffffffffffffffff811115610a9357610a926108e4565b5b610a9f86828701610a01565b925050604084013567ffffffffffffffff811115610ac057610abf6108e4565b5b610acc86828701610a01565b9150509250925092565b60008160030b9050919050565b610aec81610ad6565b82525050565b6000602082019050610b076000830184610ae3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b47578082015181840152602081019050610b2c565b83811115610b56576000848401525b50505050565b6000610b6782610b0d565b610b718185610b18565b9350610b81818560208601610b29565b610b8a816108f3565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600082825260208201905092915050565b6000610bdd82610b0d565b610be78185610bc1565b9350610bf7818560208601610b29565b610c00816108f3565b840191505092915050565b6000610c178383610bd2565b905092915050565b6000602082019050919050565b6000610c3782610b95565b610c418185610ba0565b935083602082028501610c5385610bb1565b8060005b85811015610c8f5784840389528151610c708582610c0b565b9450610c7b83610c1f565b925060208a01995050600181019050610c57565b50829750879550505050505092915050565b60006040820190508181036000830152610cbb8185610b5c565b90508181036020830152610ccf8184610c2c565b90509392505050565b600067ffffffffffffffff821115610cf357610cf2610904565b5b602082029050602081019050919050565b600080fd5b6000610d1c610d1784610cd8565b610964565b90508083825260208201905060208402830185811115610d3f57610d3e610d04565b5b835b81811015610d8657803567ffffffffffffffff811115610d6457610d636108e9565b5b808601610d718982610a01565b85526020850194505050602081019050610d41565b5050509392505050565b600082601f830112610da557610da46108e9565b5b8135610db5848260208601610d09565b91505092915050565b600080600060608486031215610dd757610dd66108df565b5b600084013567ffffffffffffffff811115610df557610df46108e4565b5b610e0186828701610a01565b935050602084013567ffffffffffffffff811115610e2257610e216108e4565b5b610e2e86828701610a01565b925050604084013567ffffffffffffffff811115610e4f57610e4e6108e4565b5b610e5b86828701610d90565b9150509250925092565b6000819050919050565b610e7881610e65565b82525050565b6000602082019050610e936000830184610e6f565b92915050565b600060208284031215610eaf57610eae6108df565b5b600082013567ffffffffffffffff811115610ecd57610ecc6108e4565b5b610ed984828501610a01565b91505092915050565b60006040820190508181036000830152610efc8185610b5c565b90508181036020830152610f108184610b5c565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b6000610f6482610b95565b610f6e8185610f48565b935083602082028501610f8085610bb1565b8060005b85811015610fbc5784840389528151610f9d8582610c0b565b9450610fa883610c1f565b925060208a01995050600181019050610f84565b50829750879550505050505092915050565b60006040830160008301518482036000860152610feb8282610bd2565b915050602083015184820360208601526110058282610f59565b9150508091505092915050565b6000602082019050818103600083015261102c8184610fce565b905092915050565b61103d81610ad6565b811461104857600080fd5b50565b60008151905061105a81611034565b92915050565b600060208284031215611076576110756108df565b5b60006110848482850161104b565b91505092915050565b6000819050919050565b60006110b26110ad6110a884610ad6565b61108d565b610e65565b9050919050565b6110c281611097565b82525050565b60006020820190506110dd60008301846110b9565b92915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000604083016000830151848203600086015261112c8282610bd2565b915050602083015184820360208601526111468282610bd2565b9150508091505092915050565b600061115f838361110f565b905092915050565b6000602082019050919050565b600061117f826110e3565b61118981856110ee565b93508360208202850161119b856110ff565b8060005b858110156111d757848403895281516111b88582611153565b94506111c383611167565b925060208a0199505060018101905061119f565b50829750879550505050505092915050565b600060408201905081810360008301526112038185610b5c565b905081810360208301526112178184611174565b90509392505050565b6000602082019050818103600083015261123a8184610b5c565b905092915050565b600080fd5b600080fd5b600061125f61125a8461097f565b610964565b90508281526020810184848401111561127b5761127a6108ee565b5b611286848285610b29565b509392505050565b600082601f8301126112a3576112a26108e9565b5b81516112b384826020860161124c565b91505092915050565b60006112cf6112ca84610cd8565b610964565b905080838252602082019050602084028301858111156112f2576112f1610d04565b5b835b8181101561133957805167ffffffffffffffff811115611317576113166108e9565b5b808601611324898261128e565b855260208501945050506020810190506112f4565b5050509392505050565b600082601f830112611358576113576108e9565b5b81516113688482602086016112bc565b91505092915050565b60006040828403121561138757611386611242565b5b6113916040610964565b9050600082015167ffffffffffffffff8111156113b1576113b0611247565b5b6113bd8482850161128e565b600083015250602082015167ffffffffffffffff8111156113e1576113e0611247565b5b6113ed84828501611343565b60208301525092915050565b60006020828403121561140f5761140e6108df565b5b600082015167ffffffffffffffff81111561142d5761142c6108e4565b5b61143984828501611371565b91505092915050565b6000604083016000830151848203600086015261145f8282610bd2565b915050602083015184820360208601526114798282610f59565b9150508091505092915050565b600060408201905081810360008301526114a08185610b5c565b905081810360208301526114b48184611442565b90509392505050565b6000604082840312156114d3576114d2611242565b5b6114dd6040610964565b9050600082015167ffffffffffffffff8111156114fd576114fc611247565b5b6115098482850161128e565b600083015250602082015167ffffffffffffffff81111561152d5761152c611247565b5b61153984828501611343565b60208301525092915050565b60006020828403121561155b5761155a6108df565b5b600082015167ffffffffffffffff811115611579576115786108e4565b5b611585848285016114bd565b9150509291505056fea2646970667358221220c4aba62bac51140354dd55b475ed3bde2226a18fc413bccf0b2fbfb1fec7c8dc64736f6c634300080b0033 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.go new file mode 100644 index 000000000..21e5ab5b6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.go @@ -0,0 +1,614 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package KeyShare + +import ( + "fmt" + "math/big" + "strings" + + "github.com/FISCO-BCOS/go-sdk/v3/abi" + "github.com/FISCO-BCOS/go-sdk/v3/abi/bind" + "github.com/FISCO-BCOS/go-sdk/v3/types" + "github.com/ethereum/go-ethereum/common" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = abi.U256 + _ = bind.Bind + _ = common.Big1 +) + +// KeyShareABI is the input ABI used to generate the binding from. +const KeyShareABI = "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"CreateResult\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"InsertResult\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"RemoveResult\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"UpdateResult\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"tableName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"fields\",\"type\":\"string[]\"}],\"name\":\"createTable\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"desc\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"secret\",\"type\":\"string\"}],\"name\":\"insert\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"remove\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"select\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"secret\",\"type\":\"string\"}],\"name\":\"update\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// KeyShareBin is the compiled bytecode used for deploying new contracts. +var KeyShareBin = "0x60806040523480156200001157600080fd5b506000600267ffffffffffffffff81111562000032576200003162000399565b5b6040519080825280602002602001820160405280156200006757816020015b6060815260200190600190039081620000515790505b5090506040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525081600081518110620000b757620000b6620003c8565b5b60200260200101819052506040518060400160405280600681526020017f7365637265740000000000000000000000000000000000000000000000000000815250816001815181106200010f576200010e620003c8565b5b6020026020010181905250600060405180604001604052806040518060400160405280600281526020017f6964000000000000000000000000000000000000000000000000000000000000815250815260200183815250905061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e6040518060400160405280600b81526020017f745f6b65795f7368617265000000000000000000000000000000000000000000815250836040518363ffffffff1660e01b8152600401620001dc92919062000605565b6020604051808303816000875af1158015620001fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000222919062000683565b50600061100273ffffffffffffffffffffffffffffffffffffffff1663f23f63c96040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b8152600401620002979190620006b5565b602060405180830381865afa158015620002b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002db91906200073e565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000347906200079a565b60405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050620007bc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081519050919050565b600082825260208201905092915050565b60005b838110156200043357808201518184015260208101905062000416565b8381111562000443576000848401525b50505050565b6000601f19601f8301169050919050565b60006200046782620003f7565b62000473818562000402565b93506200048581856020860162000413565b620004908162000449565b840191505092915050565b600082825260208201905092915050565b6000620004b982620003f7565b620004c581856200049b565b9350620004d781856020860162000413565b620004e28162000449565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000620005278383620004ac565b905092915050565b6000602082019050919050565b60006200054982620004ed565b620005558185620004f8565b935083602082028501620005698562000509565b8060005b85811015620005ab578484038952815162000589858262000519565b945062000596836200052f565b925060208a019950506001810190506200056d565b50829750879550505050505092915050565b60006040830160008301518482036000860152620005dc8282620004ac565b91505060208301518482036020860152620005f882826200053c565b9150508091505092915050565b600060408201905081810360008301526200062181856200045a565b90508181036020830152620006378184620005bd565b90509392505050565b600080fd5b60008160030b9050919050565b6200065d8162000645565b81146200066957600080fd5b50565b6000815190506200067d8162000652565b92915050565b6000602082840312156200069c576200069b62000640565b5b6000620006ac848285016200066c565b91505092915050565b60006020820190508181036000830152620006d181846200045a565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200070682620006d9565b9050919050565b6200071881620006f9565b81146200072457600080fd5b50565b60008151905062000738816200070d565b92915050565b60006020828403121562000757576200075662000640565b5b6000620007678482850162000727565b91505092915050565b50565b60006200078260008362000402565b91506200078f8262000770565b600082019050919050565b60006020820190508181036000830152620007b58162000773565b9050919050565b6115c480620007cc6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80632fe99bdc1461006757806331c3e4561461009757806355f150f1146100c75780636a5bae4e146100e657806380599e4b14610116578063fcd7e3c114610146575b600080fd5b610081600480360381019061007c9190610a2f565b610177565b60405161008e9190610af2565b60405180910390f35b6100b160048036038101906100ac9190610a2f565b610309565b6040516100be9190610af2565b60405180910390f35b6100cf61051b565b6040516100dd929190610ca1565b60405180910390f35b61010060048036038101906100fb9190610dbe565b6105eb565b60405161010d9190610e7e565b60405180910390f35b610130600480360381019061012b9190610e99565b6106cf565b60405161013d9190610af2565b60405180910390f35b610160600480360381019061015b9190610e99565b6107b0565b60405161016e929190610ee2565b60405180910390f35b600080600267ffffffffffffffff81111561019557610194610904565b5b6040519080825280602002602001820160405280156101c857816020015b60608152602001906001900390816101b35790505b50905083816000815181106101e0576101df610f19565b5b60200260200101819052508281600181518110610200576101ff610f19565b5b60200260200101819052506000604051806040016040528087815260200183815250905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635c6e105f836040518263ffffffff1660e01b81526004016102809190611012565b6020604051808303816000875af115801561029f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c39190611060565b90507fc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce816040516102f491906110c8565b60405180910390a18093505050509392505050565b600080600267ffffffffffffffff81111561032757610326610904565b5b60405190808252806020026020018201604052801561036057816020015b61034d6108bb565b8152602001906001900390816103455790505b50905060405180604001604052806040518060400160405280600481526020017f6e616d6500000000000000000000000000000000000000000000000000000000815250815260200185815250816000815181106103c1576103c0610f19565b5b602002602001018190525060405180604001604052806040518060400160405280600681526020017f73656372657400000000000000000000000000000000000000000000000000008152508152602001848152508160018151811061042a57610429610f19565b5b602002602001018190525060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166341ffd75f87846040518363ffffffff1660e01b81526004016104939291906111e9565b6020604051808303816000875af11580156104b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d69190611060565b90507f8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a8160405161050791906110c8565b60405180910390a180925050509392505050565b606080600061100273ffffffffffffffffffffffffffffffffffffffff16635d0d6d546040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016105909190611220565b600060405180830381865afa1580156105ad573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906105d691906113f9565b90508060000151816020015192509250509091565b6000806040518060400160405280858152602001848152509050600061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e87846040518363ffffffff1660e01b8152600401610644929190611486565b6020604051808303816000875af1158015610663573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106879190611060565b90507fb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210816040516106b891906110c8565b60405180910390a18060030b925050509392505050565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166380599e4b846040518263ffffffff1660e01b815260040161072b9190611220565b6020604051808303816000875af115801561074a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076e9190611060565b90507f4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc31573581128160405161079f91906110c8565b60405180910390a180915050919050565b60608060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fcd7e3c1856040518263ffffffff1660e01b815260040161080f9190611220565b600060405180830381865afa15801561082c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906108559190611545565b9050606080600283602001515114156108ad5782602001516000815181106108805761087f610f19565b5b6020026020010151915082602001516001815181106108a2576108a1610f19565b5b602002602001015190505b818194509450505050915091565b604051806040016040528060608152602001606081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61093c826108f3565b810181811067ffffffffffffffff8211171561095b5761095a610904565b5b80604052505050565b600061096e6108d5565b905061097a8282610933565b919050565b600067ffffffffffffffff82111561099a57610999610904565b5b6109a3826108f3565b9050602081019050919050565b82818337600083830152505050565b60006109d26109cd8461097f565b610964565b9050828152602081018484840111156109ee576109ed6108ee565b5b6109f98482856109b0565b509392505050565b600082601f830112610a1657610a156108e9565b5b8135610a268482602086016109bf565b91505092915050565b600080600060608486031215610a4857610a476108df565b5b600084013567ffffffffffffffff811115610a6657610a656108e4565b5b610a7286828701610a01565b935050602084013567ffffffffffffffff811115610a9357610a926108e4565b5b610a9f86828701610a01565b925050604084013567ffffffffffffffff811115610ac057610abf6108e4565b5b610acc86828701610a01565b9150509250925092565b60008160030b9050919050565b610aec81610ad6565b82525050565b6000602082019050610b076000830184610ae3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b47578082015181840152602081019050610b2c565b83811115610b56576000848401525b50505050565b6000610b6782610b0d565b610b718185610b18565b9350610b81818560208601610b29565b610b8a816108f3565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600082825260208201905092915050565b6000610bdd82610b0d565b610be78185610bc1565b9350610bf7818560208601610b29565b610c00816108f3565b840191505092915050565b6000610c178383610bd2565b905092915050565b6000602082019050919050565b6000610c3782610b95565b610c418185610ba0565b935083602082028501610c5385610bb1565b8060005b85811015610c8f5784840389528151610c708582610c0b565b9450610c7b83610c1f565b925060208a01995050600181019050610c57565b50829750879550505050505092915050565b60006040820190508181036000830152610cbb8185610b5c565b90508181036020830152610ccf8184610c2c565b90509392505050565b600067ffffffffffffffff821115610cf357610cf2610904565b5b602082029050602081019050919050565b600080fd5b6000610d1c610d1784610cd8565b610964565b90508083825260208201905060208402830185811115610d3f57610d3e610d04565b5b835b81811015610d8657803567ffffffffffffffff811115610d6457610d636108e9565b5b808601610d718982610a01565b85526020850194505050602081019050610d41565b5050509392505050565b600082601f830112610da557610da46108e9565b5b8135610db5848260208601610d09565b91505092915050565b600080600060608486031215610dd757610dd66108df565b5b600084013567ffffffffffffffff811115610df557610df46108e4565b5b610e0186828701610a01565b935050602084013567ffffffffffffffff811115610e2257610e216108e4565b5b610e2e86828701610a01565b925050604084013567ffffffffffffffff811115610e4f57610e4e6108e4565b5b610e5b86828701610d90565b9150509250925092565b6000819050919050565b610e7881610e65565b82525050565b6000602082019050610e936000830184610e6f565b92915050565b600060208284031215610eaf57610eae6108df565b5b600082013567ffffffffffffffff811115610ecd57610ecc6108e4565b5b610ed984828501610a01565b91505092915050565b60006040820190508181036000830152610efc8185610b5c565b90508181036020830152610f108184610b5c565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b6000610f6482610b95565b610f6e8185610f48565b935083602082028501610f8085610bb1565b8060005b85811015610fbc5784840389528151610f9d8582610c0b565b9450610fa883610c1f565b925060208a01995050600181019050610f84565b50829750879550505050505092915050565b60006040830160008301518482036000860152610feb8282610bd2565b915050602083015184820360208601526110058282610f59565b9150508091505092915050565b6000602082019050818103600083015261102c8184610fce565b905092915050565b61103d81610ad6565b811461104857600080fd5b50565b60008151905061105a81611034565b92915050565b600060208284031215611076576110756108df565b5b60006110848482850161104b565b91505092915050565b6000819050919050565b60006110b26110ad6110a884610ad6565b61108d565b610e65565b9050919050565b6110c281611097565b82525050565b60006020820190506110dd60008301846110b9565b92915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000604083016000830151848203600086015261112c8282610bd2565b915050602083015184820360208601526111468282610bd2565b9150508091505092915050565b600061115f838361110f565b905092915050565b6000602082019050919050565b600061117f826110e3565b61118981856110ee565b93508360208202850161119b856110ff565b8060005b858110156111d757848403895281516111b88582611153565b94506111c383611167565b925060208a0199505060018101905061119f565b50829750879550505050505092915050565b600060408201905081810360008301526112038185610b5c565b905081810360208301526112178184611174565b90509392505050565b6000602082019050818103600083015261123a8184610b5c565b905092915050565b600080fd5b600080fd5b600061125f61125a8461097f565b610964565b90508281526020810184848401111561127b5761127a6108ee565b5b611286848285610b29565b509392505050565b600082601f8301126112a3576112a26108e9565b5b81516112b384826020860161124c565b91505092915050565b60006112cf6112ca84610cd8565b610964565b905080838252602082019050602084028301858111156112f2576112f1610d04565b5b835b8181101561133957805167ffffffffffffffff811115611317576113166108e9565b5b808601611324898261128e565b855260208501945050506020810190506112f4565b5050509392505050565b600082601f830112611358576113576108e9565b5b81516113688482602086016112bc565b91505092915050565b60006040828403121561138757611386611242565b5b6113916040610964565b9050600082015167ffffffffffffffff8111156113b1576113b0611247565b5b6113bd8482850161128e565b600083015250602082015167ffffffffffffffff8111156113e1576113e0611247565b5b6113ed84828501611343565b60208301525092915050565b60006020828403121561140f5761140e6108df565b5b600082015167ffffffffffffffff81111561142d5761142c6108e4565b5b61143984828501611371565b91505092915050565b6000604083016000830151848203600086015261145f8282610bd2565b915050602083015184820360208601526114798282610f59565b9150508091505092915050565b600060408201905081810360008301526114a08185610b5c565b905081810360208301526114b48184611442565b90509392505050565b6000604082840312156114d3576114d2611242565b5b6114dd6040610964565b9050600082015167ffffffffffffffff8111156114fd576114fc611247565b5b6115098482850161128e565b600083015250602082015167ffffffffffffffff81111561152d5761152c611247565b5b61153984828501611343565b60208301525092915050565b60006020828403121561155b5761155a6108df565b5b600082015167ffffffffffffffff811115611579576115786108e4565b5b611585848285016114bd565b9150509291505056fea2646970667358221220c4aba62bac51140354dd55b475ed3bde2226a18fc413bccf0b2fbfb1fec7c8dc64736f6c634300080b0033" +var KeyShareSMBin = "0x" + +// DeployKeyShare deploys a new contract, binding an instance of KeyShare to it. +func DeployKeyShare(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Receipt, *KeyShare, error) { + parsed, err := abi.JSON(strings.NewReader(KeyShareABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(KeyShareSMBin) + } else { + bytecode = common.FromHex(KeyShareBin) + } + if len(bytecode) == 0 { + return common.Address{}, nil, nil, fmt.Errorf("cannot deploy empty bytecode") + } + address, receipt, contract, err := bind.DeployContract(auth, parsed, bytecode, KeyShareABI, backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, receipt, &KeyShare{KeyShareCaller: KeyShareCaller{contract: contract}, KeyShareTransactor: KeyShareTransactor{contract: contract}, KeyShareFilterer: KeyShareFilterer{contract: contract}}, nil +} + +func AsyncDeployKeyShare(auth *bind.TransactOpts, handler func(*types.Receipt, error), backend bind.ContractBackend) (*types.Transaction, error) { + parsed, err := abi.JSON(strings.NewReader(KeyShareABI)) + if err != nil { + return nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(KeyShareSMBin) + } else { + bytecode = common.FromHex(KeyShareBin) + } + if len(bytecode) == 0 { + return nil, fmt.Errorf("cannot deploy empty bytecode") + } + tx, err := bind.AsyncDeployContract(auth, handler, parsed, bytecode, KeyShareABI, backend) + if err != nil { + return nil, err + } + return tx, nil +} + +// KeyShare is an auto generated Go binding around a Solidity contract. +type KeyShare struct { + KeyShareCaller // Read-only binding to the contract + KeyShareTransactor // Write-only binding to the contract + KeyShareFilterer // Log filterer for contract events +} + +// KeyShareCaller is an auto generated read-only Go binding around a Solidity contract. +type KeyShareCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// KeyShareTransactor is an auto generated write-only Go binding around a Solidity contract. +type KeyShareTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// KeyShareFilterer is an auto generated log filtering Go binding around a Solidity contract events. +type KeyShareFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// KeyShareSession is an auto generated Go binding around a Solidity contract, +// with pre-set call and transact options. +type KeyShareSession struct { + Contract *KeyShare // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// KeyShareCallerSession is an auto generated read-only Go binding around a Solidity contract, +// with pre-set call options. +type KeyShareCallerSession struct { + Contract *KeyShareCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// KeyShareTransactorSession is an auto generated write-only Go binding around a Solidity contract, +// with pre-set transact options. +type KeyShareTransactorSession struct { + Contract *KeyShareTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// KeyShareRaw is an auto generated low-level Go binding around a Solidity contract. +type KeyShareRaw struct { + Contract *KeyShare // Generic contract binding to access the raw methods on +} + +// KeyShareCallerRaw is an auto generated low-level read-only Go binding around a Solidity contract. +type KeyShareCallerRaw struct { + Contract *KeyShareCaller // Generic read-only contract binding to access the raw methods on +} + +// KeyShareTransactorRaw is an auto generated low-level write-only Go binding around a Solidity contract. +type KeyShareTransactorRaw struct { + Contract *KeyShareTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewKeyShare creates a new instance of KeyShare, bound to a specific deployed contract. +func NewKeyShare(address common.Address, backend bind.ContractBackend) (*KeyShare, error) { + contract, err := bindKeyShare(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &KeyShare{KeyShareCaller: KeyShareCaller{contract: contract}, KeyShareTransactor: KeyShareTransactor{contract: contract}, KeyShareFilterer: KeyShareFilterer{contract: contract}}, nil +} + +// NewKeyShareCaller creates a new read-only instance of KeyShare, bound to a specific deployed contract. +func NewKeyShareCaller(address common.Address, caller bind.ContractCaller) (*KeyShareCaller, error) { + contract, err := bindKeyShare(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &KeyShareCaller{contract: contract}, nil +} + +// NewKeyShareTransactor creates a new write-only instance of KeyShare, bound to a specific deployed contract. +func NewKeyShareTransactor(address common.Address, transactor bind.ContractTransactor) (*KeyShareTransactor, error) { + contract, err := bindKeyShare(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &KeyShareTransactor{contract: contract}, nil +} + +// NewKeyShareFilterer creates a new log filterer instance of KeyShare, bound to a specific deployed contract. +func NewKeyShareFilterer(address common.Address, filterer bind.ContractFilterer) (*KeyShareFilterer, error) { + contract, err := bindKeyShare(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &KeyShareFilterer{contract: contract}, nil +} + +// bindKeyShare binds a generic wrapper to an already deployed contract. +func bindKeyShare(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(KeyShareABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_KeyShare *KeyShareRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _KeyShare.Contract.KeyShareCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_KeyShare *KeyShareRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.KeyShareTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_KeyShare *KeyShareRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.KeyShareTransactor.contract.TransactWithResult(opts, result, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_KeyShare *KeyShareCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _KeyShare.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_KeyShare *KeyShareTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_KeyShare *KeyShareTransactorRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.contract.TransactWithResult(opts, result, method, params...) +} + +// Desc is a free data retrieval call binding the contract method 0x55f150f1. +// +// Solidity: function desc() constant returns(string, string[]) +func (_KeyShare *KeyShareCaller) Desc(opts *bind.CallOpts) (string, []string, error) { + var ( + ret0 = new(string) + ret1 = new([]string) + ) + out := &[]interface{}{ + ret0, + ret1, + } + err := _KeyShare.contract.Call(opts, out, "desc") + return *ret0, *ret1, err +} + +// Desc is a free data retrieval call binding the contract method 0x55f150f1. +// +// Solidity: function desc() constant returns(string, string[]) +func (_KeyShare *KeyShareSession) Desc() (string, []string, error) { + return _KeyShare.Contract.Desc(&_KeyShare.CallOpts) +} + +// Desc is a free data retrieval call binding the contract method 0x55f150f1. +// +// Solidity: function desc() constant returns(string, string[]) +func (_KeyShare *KeyShareCallerSession) Desc() (string, []string, error) { + return _KeyShare.Contract.Desc(&_KeyShare.CallOpts) +} + +// Select is a free data retrieval call binding the contract method 0xfcd7e3c1. +// +// Solidity: function select(string id) constant returns(string, string) +func (_KeyShare *KeyShareCaller) Select(opts *bind.CallOpts, id string) (string, string, error) { + var ( + ret0 = new(string) + ret1 = new(string) + ) + out := &[]interface{}{ + ret0, + ret1, + } + err := _KeyShare.contract.Call(opts, out, "select", id) + return *ret0, *ret1, err +} + +// Select is a free data retrieval call binding the contract method 0xfcd7e3c1. +// +// Solidity: function select(string id) constant returns(string, string) +func (_KeyShare *KeyShareSession) Select(id string) (string, string, error) { + return _KeyShare.Contract.Select(&_KeyShare.CallOpts, id) +} + +// Select is a free data retrieval call binding the contract method 0xfcd7e3c1. +// +// Solidity: function select(string id) constant returns(string, string) +func (_KeyShare *KeyShareCallerSession) Select(id string) (string, string, error) { + return _KeyShare.Contract.Select(&_KeyShare.CallOpts, id) +} + +// CreateTable is a paid mutator transaction binding the contract method 0x6a5bae4e. +// +// Solidity: function createTable(string tableName, string key, string[] fields) returns(int256) +func (_KeyShare *KeyShareTransactor) CreateTable(opts *bind.TransactOpts, tableName string, key string, fields []string) (*big.Int, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "createTable", tableName, key, fields) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncCreateTable(handler func(*types.Receipt, error), opts *bind.TransactOpts, tableName string, key string, fields []string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "createTable", tableName, key, fields) +} + +// CreateTable is a paid mutator transaction binding the contract method 0x6a5bae4e. +// +// Solidity: function createTable(string tableName, string key, string[] fields) returns(int256) +func (_KeyShare *KeyShareSession) CreateTable(tableName string, key string, fields []string) (*big.Int, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.CreateTable(&_KeyShare.TransactOpts, tableName, key, fields) +} + +func (_KeyShare *KeyShareSession) AsyncCreateTable(handler func(*types.Receipt, error), tableName string, key string, fields []string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncCreateTable(handler, &_KeyShare.TransactOpts, tableName, key, fields) +} + +// CreateTable is a paid mutator transaction binding the contract method 0x6a5bae4e. +// +// Solidity: function createTable(string tableName, string key, string[] fields) returns(int256) +func (_KeyShare *KeyShareTransactorSession) CreateTable(tableName string, key string, fields []string) (*big.Int, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.CreateTable(&_KeyShare.TransactOpts, tableName, key, fields) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncCreateTable(handler func(*types.Receipt, error), tableName string, key string, fields []string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncCreateTable(handler, &_KeyShare.TransactOpts, tableName, key, fields) +} + +// Insert is a paid mutator transaction binding the contract method 0x2fe99bdc. +// +// Solidity: function insert(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactor) Insert(opts *bind.TransactOpts, id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(int32) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "insert", id, name, secret) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncInsert(handler func(*types.Receipt, error), opts *bind.TransactOpts, id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "insert", id, name, secret) +} + +// Insert is a paid mutator transaction binding the contract method 0x2fe99bdc. +// +// Solidity: function insert(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareSession) Insert(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Insert(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareSession) AsyncInsert(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncInsert(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// Insert is a paid mutator transaction binding the contract method 0x2fe99bdc. +// +// Solidity: function insert(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactorSession) Insert(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Insert(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncInsert(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncInsert(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// Remove is a paid mutator transaction binding the contract method 0x80599e4b. +// +// Solidity: function remove(string id) returns(int32) +func (_KeyShare *KeyShareTransactor) Remove(opts *bind.TransactOpts, id string) (int32, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(int32) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "remove", id) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncRemove(handler func(*types.Receipt, error), opts *bind.TransactOpts, id string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "remove", id) +} + +// Remove is a paid mutator transaction binding the contract method 0x80599e4b. +// +// Solidity: function remove(string id) returns(int32) +func (_KeyShare *KeyShareSession) Remove(id string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Remove(&_KeyShare.TransactOpts, id) +} + +func (_KeyShare *KeyShareSession) AsyncRemove(handler func(*types.Receipt, error), id string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncRemove(handler, &_KeyShare.TransactOpts, id) +} + +// Remove is a paid mutator transaction binding the contract method 0x80599e4b. +// +// Solidity: function remove(string id) returns(int32) +func (_KeyShare *KeyShareTransactorSession) Remove(id string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Remove(&_KeyShare.TransactOpts, id) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncRemove(handler func(*types.Receipt, error), id string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncRemove(handler, &_KeyShare.TransactOpts, id) +} + +// Update is a paid mutator transaction binding the contract method 0x31c3e456. +// +// Solidity: function update(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactor) Update(opts *bind.TransactOpts, id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(int32) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "update", id, name, secret) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncUpdate(handler func(*types.Receipt, error), opts *bind.TransactOpts, id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "update", id, name, secret) +} + +// Update is a paid mutator transaction binding the contract method 0x31c3e456. +// +// Solidity: function update(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareSession) Update(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Update(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareSession) AsyncUpdate(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncUpdate(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// Update is a paid mutator transaction binding the contract method 0x31c3e456. +// +// Solidity: function update(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactorSession) Update(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Update(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncUpdate(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncUpdate(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// KeyShareCreateResult represents a CreateResult event raised by the KeyShare contract. +type KeyShareCreateResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchCreateResult is a free log subscription operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "CreateResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "CreateResult") +} + +// ParseCreateResult is a log parse operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseCreateResult(log types.Log) (*KeyShareCreateResult, error) { + event := new(KeyShareCreateResult) + if err := _KeyShare.contract.UnpackLog(event, "CreateResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchCreateResult is a free log subscription operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareSession) WatchCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchCreateResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllCreateResult(fromBlock, handler) +} + +// ParseCreateResult is a log parse operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareSession) ParseCreateResult(log types.Log) (*KeyShareCreateResult, error) { + return _KeyShare.Contract.ParseCreateResult(log) +} + +// KeyShareInsertResult represents a InsertResult event raised by the KeyShare contract. +type KeyShareInsertResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchInsertResult is a free log subscription operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "InsertResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "InsertResult") +} + +// ParseInsertResult is a log parse operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseInsertResult(log types.Log) (*KeyShareInsertResult, error) { + event := new(KeyShareInsertResult) + if err := _KeyShare.contract.UnpackLog(event, "InsertResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchInsertResult is a free log subscription operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareSession) WatchInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchInsertResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllInsertResult(fromBlock, handler) +} + +// ParseInsertResult is a log parse operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareSession) ParseInsertResult(log types.Log) (*KeyShareInsertResult, error) { + return _KeyShare.Contract.ParseInsertResult(log) +} + +// KeyShareRemoveResult represents a RemoveResult event raised by the KeyShare contract. +type KeyShareRemoveResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchRemoveResult is a free log subscription operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "RemoveResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "RemoveResult") +} + +// ParseRemoveResult is a log parse operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseRemoveResult(log types.Log) (*KeyShareRemoveResult, error) { + event := new(KeyShareRemoveResult) + if err := _KeyShare.contract.UnpackLog(event, "RemoveResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchRemoveResult is a free log subscription operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareSession) WatchRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchRemoveResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllRemoveResult(fromBlock, handler) +} + +// ParseRemoveResult is a log parse operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareSession) ParseRemoveResult(log types.Log) (*KeyShareRemoveResult, error) { + return _KeyShare.Contract.ParseRemoveResult(log) +} + +// KeyShareUpdateResult represents a UpdateResult event raised by the KeyShare contract. +type KeyShareUpdateResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchUpdateResult is a free log subscription operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "UpdateResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "UpdateResult") +} + +// ParseUpdateResult is a log parse operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseUpdateResult(log types.Log) (*KeyShareUpdateResult, error) { + event := new(KeyShareUpdateResult) + if err := _KeyShare.contract.UnpackLog(event, "UpdateResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchUpdateResult is a free log subscription operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareSession) WatchUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchUpdateResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllUpdateResult(fromBlock, handler) +} + +// ParseUpdateResult is a log parse operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareSession) ParseUpdateResult(log types.Log) (*KeyShareUpdateResult, error) { + return _KeyShare.Contract.ParseUpdateResult(log) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.sol new file mode 100644 index 000000000..d30783026 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/KeyShare.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +import "./Table.sol"; + +contract KeyShare { + event CreateResult(int256 count); + event InsertResult(int256 count); + event UpdateResult(int256 count); + event RemoveResult(int256 count); + + TableManager constant tm = TableManager(address(0x1002)); + Table table; + string constant TABLE_NAME = "t_key_share"; + constructor () public{ + // create table + string[] memory columnNames = new string[](2); + columnNames[0] = "name"; + columnNames[1] = "secret"; + TableInfo memory tf = TableInfo("id", columnNames); + + tm.createTable(TABLE_NAME, tf); + address t_address = tm.openTable(TABLE_NAME); + require(t_address!=address(0x0),""); + table = Table(t_address); + } + + function select(string memory id) public view returns (string memory,string memory) + { + Entry memory entry = table.select(id); + + string memory name; + string memory secret; + if(entry.fields.length==2){ + name = entry.fields[0]; + secret = entry.fields[1]; + } + return (name,secret); + } + + function insert(string memory id,string memory name,string memory secret) public returns (int32){ + string[] memory columns = new string[](2); + columns[0] = name; + columns[1] = secret; + Entry memory entry = Entry(id, columns); + int32 result = table.insert(entry); + emit InsertResult(result); + return result; + } + + function update(string memory id, string memory name, string memory secret) public returns (int32){ + UpdateField[] memory updateFields = new UpdateField[](2); + updateFields[0] = UpdateField("name", name); + updateFields[1] = UpdateField("secret", secret); + + int32 result = table.update(id, updateFields); + emit UpdateResult(result); + return result; + } + + function remove(string memory id) public returns(int32){ + int32 result = table.remove(id); + emit RemoveResult(result); + return result; + } + + function createTable(string memory tableName,string memory key,string[] memory fields) public returns(int256){ + TableInfo memory tf = TableInfo(key, fields); + int32 result = tm.createTable(tableName,tf); + emit CreateResult(result); + return result; + } + + function desc() public view returns(string memory, string[] memory){ + TableInfo memory ti = tm.desc(TABLE_NAME); + return (ti.keyColumn,ti.valueColumns); + } +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.abi new file mode 100644 index 000000000..900206549 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.abi @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"}],"name":"count","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"internalType":"struct Entry","name":"entry","type":"tuple"}],"name":"insert","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"},{"components":[{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint32","name":"count","type":"uint32"}],"internalType":"struct Limit","name":"limit","type":"tuple"}],"name":"remove","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"remove","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"},{"components":[{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint32","name":"count","type":"uint32"}],"internalType":"struct Limit","name":"limit","type":"tuple"}],"name":"select","outputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"internalType":"struct Entry[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"select","outputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"internalType":"struct Entry","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"components":[{"internalType":"string","name":"columnName","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct UpdateField[]","name":"updateFields","type":"tuple[]"}],"name":"update","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"},{"components":[{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint32","name":"count","type":"uint32"}],"internalType":"struct Limit","name":"limit","type":"tuple"},{"components":[{"internalType":"string","name":"columnName","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct UpdateField[]","name":"updateFields","type":"tuple[]"}],"name":"update","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.bin new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.sol new file mode 100644 index 000000000..65169a9e6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/Table.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// 该接口文件定义了FISCO BCOS v3.1.0及以前版本的接口,使用时需要将该文件放在合约目录下 +// 若要使用FISCO BCOS v3.2.0及以后版本的接口,请使用TableV320.sol,旧合约仍然能在新节点中使用 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字 +// enum KeyOrder {Lexicographic, Numerical} + struct TableInfo { + string keyColumn; + string[] valueColumns; + } + +// 记录,用于select和insert + struct Entry { + string key; + string[] fields; // 考虑2.0的Entry接口,临时Precompiled的问题,考虑加工具类接口 + } + +// 更新字段,用于update + struct UpdateField { + string columnName; + // 考虑工具类 + string value; + } + +// 筛选条件,大于、大于等于、小于、小于等于 + enum ConditionOP {GT, GE, LT, LE} + struct Condition { + ConditionOP op; + // string field; + string value; + } + +// 数量限制 + struct Limit { + uint32 offset; + // count limit max is 500 + uint32 count; + } + +// 表管理合约,是静态Precompiled,有固定的合约地址 +abstract contract TableManager { + // 创建表,传入TableInfo + function createTable(string memory path, TableInfo memory tableInfo) public virtual returns (int32); + + // 创建KV表,传入key和value字段名 + function createKVTable(string memory tableName, string memory keyField, string memory valueField) public virtual returns (int32); + + // 只提供给Solidity合约调用时使用 + function openTable(string memory path) public view virtual returns (address); + + // 变更表字段 + // 只能新增字段,不能删除字段,新增的字段默认值为空,不能与原有字段重复 + function appendColumns(string memory path, string[] memory newColumns) public virtual returns (int32); + + // 获取表信息 + function desc(string memory tableName) public view virtual returns (TableInfo memory); +} + +// 表合约,是动态Precompiled,TableManager创建时指定地址 +abstract contract Table { + // 按key查询entry + function select(string memory key) public virtual view returns (Entry memory); + + // 按条件批量查询entry,condition为空则查询所有记录 + function select(Condition[] memory conditions, Limit memory limit) public virtual view returns (Entry[] memory); + + // 按照条件查询count数据 + function count(Condition[] memory conditions) public virtual view returns (uint32); + + // 插入数据 + function insert(Entry memory entry) public virtual returns (int32); + + // 按key更新entry + function update(string memory key, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按条件批量更新entry,condition为空则更新所有记录 + function update(Condition[] memory conditions, Limit memory limit, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按key删除entry + function remove(string memory key) public virtual returns (int32); + // 按条件批量删除entry,condition为空则删除所有记录 + function remove(Condition[] memory conditions, Limit memory limit) public virtual returns (int32); +} + +abstract contract KVTable { + function get(string memory key) public view virtual returns (bool, string memory); + + function set(string memory key, string memory value) public virtual returns (int32); +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/TableManager.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/TableManager.abi new file mode 100644 index 000000000..52e130933 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/TableManager.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"path","type":"string"},{"internalType":"string[]","name":"newColumns","type":"string[]"}],"name":"appendColumns","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tableName","type":"string"},{"internalType":"string","name":"keyField","type":"string"},{"internalType":"string","name":"valueField","type":"string"}],"name":"createKVTable","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"path","type":"string"},{"components":[{"internalType":"string","name":"keyColumn","type":"string"},{"internalType":"string[]","name":"valueColumns","type":"string[]"}],"internalType":"struct TableInfo","name":"tableInfo","type":"tuple"}],"name":"createTable","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tableName","type":"string"}],"name":"desc","outputs":[{"components":[{"internalType":"string","name":"keyColumn","type":"string"},{"internalType":"string[]","name":"valueColumns","type":"string[]"}],"internalType":"struct TableInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"path","type":"string"}],"name":"openTable","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/TableManager.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/KeyShare/TableManager.bin new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/README.md new file mode 100644 index 000000000..36e587321 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/README.md @@ -0,0 +1,26 @@ +# DecisionStorage + +合约名 `DecisionStorage`,用于在区块链上存储和管理决策记录 + +- **Decision**: 用于表示单个决策记录,包含以下字段: + - `evidence`: 证据的字符串表示。 + - `nodeID`: 节点的唯一标识符。 + - `timestamp`: 决策记录的时间戳。 + - `approved`: 表示决策是否被批准的布尔值。 + +# KeyShare + +`KeyShare`,用于在区块链上管理密钥共享信息。使用 `Table` 外部合约来实现数据存储和管理。以下是合约的主要功能和结构: + +### 事件 + +- **CreateResult**: 创建表的结果。 +- **InsertResult**: 插入记录的结果。 +- **UpdateResult**: 更新记录的结果。 +- **RemoveResult**: 移除记录的结果。 + +### 状态变量 + +- **tm**: `TableManager` 合约的地址,用于管理表。 +- **table**: 当前操作的表。 +- **TABLE_NAME**: 表的名称,固定为 `"t_key_share"`。 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/DecisionStorage.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/DecisionStorage.sol new file mode 100644 index 000000000..19c90c817 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/DecisionStorage.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.10 <0.8.20; + +contract DecisionStorage { + // 决策记录结构 + struct Decision { + string evidence; // 证据 + string nodeID; // 节点ID + uint256 timestamp; // 时间戳 + bool approved; // 是否批准 + } + + // 存储所有决策记录 + Decision[] public decisions; + + // 记录决策的事件 + event DecisionRecorded( + string evidence, + string nodeID, + uint256 timestamp, + bool approved + ); + + function recordDecision(string memory evidence, string memory nodeID, bool approved) public returns (bool) { + Decision memory newDecision = Decision({ + evidence: evidence, + nodeID: nodeID, + timestamp: block.timestamp, + approved: approved + }); + + // 存储决策 + decisions.push(newDecision); + + // 触发事件 + emit DecisionRecorded( + newDecision.evidence, + newDecision.nodeID, + newDecision.timestamp, + newDecision.approved + ); + + return true; + } + + // 验证证据 可以添加具体的验证逻辑 + function verifyEvidence(string memory evidence) public view returns (bool) { + // 目前返回true用于测试 + return true; + } + + // 获取决策记录数量 + function getDecisionCount() public view returns (uint256) { + return decisions.length; + } + + // 获取指定索引的决策记录 + function getDecision(uint256 index) public view returns ( + string memory evidence, + string memory nodeID, + uint256 timestamp, + bool approved + ) { + require(index < decisions.length, "Index out of bounds"); + Decision memory decision = decisions[index]; + return ( + decision.evidence, + decision.nodeID, + decision.timestamp, + decision.approved + ); + } + +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/KeyShare.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/KeyShare.sol new file mode 100644 index 000000000..d30783026 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/KeyShare.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +import "./Table.sol"; + +contract KeyShare { + event CreateResult(int256 count); + event InsertResult(int256 count); + event UpdateResult(int256 count); + event RemoveResult(int256 count); + + TableManager constant tm = TableManager(address(0x1002)); + Table table; + string constant TABLE_NAME = "t_key_share"; + constructor () public{ + // create table + string[] memory columnNames = new string[](2); + columnNames[0] = "name"; + columnNames[1] = "secret"; + TableInfo memory tf = TableInfo("id", columnNames); + + tm.createTable(TABLE_NAME, tf); + address t_address = tm.openTable(TABLE_NAME); + require(t_address!=address(0x0),""); + table = Table(t_address); + } + + function select(string memory id) public view returns (string memory,string memory) + { + Entry memory entry = table.select(id); + + string memory name; + string memory secret; + if(entry.fields.length==2){ + name = entry.fields[0]; + secret = entry.fields[1]; + } + return (name,secret); + } + + function insert(string memory id,string memory name,string memory secret) public returns (int32){ + string[] memory columns = new string[](2); + columns[0] = name; + columns[1] = secret; + Entry memory entry = Entry(id, columns); + int32 result = table.insert(entry); + emit InsertResult(result); + return result; + } + + function update(string memory id, string memory name, string memory secret) public returns (int32){ + UpdateField[] memory updateFields = new UpdateField[](2); + updateFields[0] = UpdateField("name", name); + updateFields[1] = UpdateField("secret", secret); + + int32 result = table.update(id, updateFields); + emit UpdateResult(result); + return result; + } + + function remove(string memory id) public returns(int32){ + int32 result = table.remove(id); + emit RemoveResult(result); + return result; + } + + function createTable(string memory tableName,string memory key,string[] memory fields) public returns(int256){ + TableInfo memory tf = TableInfo(key, fields); + int32 result = tm.createTable(tableName,tf); + emit CreateResult(result); + return result; + } + + function desc() public view returns(string memory, string[] memory){ + TableInfo memory ti = tm.desc(TABLE_NAME); + return (ti.keyColumn,ti.valueColumns); + } +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/Table.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/Table.sol new file mode 100644 index 000000000..65169a9e6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Contracts/source/Table.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// 该接口文件定义了FISCO BCOS v3.1.0及以前版本的接口,使用时需要将该文件放在合约目录下 +// 若要使用FISCO BCOS v3.2.0及以后版本的接口,请使用TableV320.sol,旧合约仍然能在新节点中使用 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字 +// enum KeyOrder {Lexicographic, Numerical} + struct TableInfo { + string keyColumn; + string[] valueColumns; + } + +// 记录,用于select和insert + struct Entry { + string key; + string[] fields; // 考虑2.0的Entry接口,临时Precompiled的问题,考虑加工具类接口 + } + +// 更新字段,用于update + struct UpdateField { + string columnName; + // 考虑工具类 + string value; + } + +// 筛选条件,大于、大于等于、小于、小于等于 + enum ConditionOP {GT, GE, LT, LE} + struct Condition { + ConditionOP op; + // string field; + string value; + } + +// 数量限制 + struct Limit { + uint32 offset; + // count limit max is 500 + uint32 count; + } + +// 表管理合约,是静态Precompiled,有固定的合约地址 +abstract contract TableManager { + // 创建表,传入TableInfo + function createTable(string memory path, TableInfo memory tableInfo) public virtual returns (int32); + + // 创建KV表,传入key和value字段名 + function createKVTable(string memory tableName, string memory keyField, string memory valueField) public virtual returns (int32); + + // 只提供给Solidity合约调用时使用 + function openTable(string memory path) public view virtual returns (address); + + // 变更表字段 + // 只能新增字段,不能删除字段,新增的字段默认值为空,不能与原有字段重复 + function appendColumns(string memory path, string[] memory newColumns) public virtual returns (int32); + + // 获取表信息 + function desc(string memory tableName) public view virtual returns (TableInfo memory); +} + +// 表合约,是动态Precompiled,TableManager创建时指定地址 +abstract contract Table { + // 按key查询entry + function select(string memory key) public virtual view returns (Entry memory); + + // 按条件批量查询entry,condition为空则查询所有记录 + function select(Condition[] memory conditions, Limit memory limit) public virtual view returns (Entry[] memory); + + // 按照条件查询count数据 + function count(Condition[] memory conditions) public virtual view returns (uint32); + + // 插入数据 + function insert(Entry memory entry) public virtual returns (int32); + + // 按key更新entry + function update(string memory key, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按条件批量更新entry,condition为空则更新所有记录 + function update(Condition[] memory conditions, Limit memory limit, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按key删除entry + function remove(string memory key) public virtual returns (int32); + // 按条件批量删除entry,condition为空则删除所有记录 + function remove(Condition[] memory conditions, Limit memory limit) public virtual returns (int32); +} + +abstract contract KVTable { + function get(string memory key) public view virtual returns (bool, string memory); + + function set(string memory key, string memory value) public virtual returns (int32); +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/.gitignore b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/.gitignore new file mode 100644 index 000000000..bccecb6e4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/.gitignore @@ -0,0 +1,8 @@ + +.DS_Store + +.idea/ + +config/sdk/ + +config/keys/ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/README.md new file mode 100644 index 000000000..2f8a73039 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/README.md @@ -0,0 +1 @@ +# hufu diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.config_HW_DEBUG_x64 b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.config_HW_DEBUG_x64 new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.cproject b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.cproject new file mode 100755 index 000000000..690134a3a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.cproject @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.project b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.project new file mode 100755 index 000000000..102c10587 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.project @@ -0,0 +1,28 @@ + + + SampleEnclave + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + org.eclipse.cdt.core.ccnature + com.intel.sgx.sgxnature + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.settings/language.settings.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.settings/language.settings.xml new file mode 100755 index 000000000..bb1f9221e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/.settings/language.settings.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.cpp new file mode 100755 index 000000000..cca3fac68 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Test Array Attributes */ + +#include "sgx_trts.h" +#include "../Enclave.h" +#include "Enclave_t.h" +#include + +/* ecall_array_user_check: + * [user_check] parameter does not perfrom copy operations. + */ +void ecall_array_user_check(int arr[4]) +{ + if (sgx_is_outside_enclave(arr, 4 * sizeof(int)) != 1) + abort(); + + for (int i = 0; i < 4; i++) { + assert(arr[i] == i); + /* Below code performs as arr[i] = (3 - i) + * It writes 4 bytes to untrusted memory, not 8 bytes aligned. + * So we need to use memcpy_verw() for security consideration. + * */ + int tmp = 3 - i; + memcpy_verw(&arr[i], &tmp, sizeof(int)); + } +} + +/* ecall_array_in: + * arr[] is copied to trusted domain, but modified + * results will not be reflected to the untrusted side. + */ +void ecall_array_in(int arr[4]) +{ + for (int i = 0; i < 4; i++) { + assert(arr[i] == i); + arr[i] = (3 - i); + } +} + +/* ecall_array_out: + * arr[] is allocated inside the enclave, and it will be copied + * to the untrusted side + */ +void ecall_array_out(int arr[4]) +{ + for (int i = 0; i < 4; i++) { + /* arr is not copied from App */ + assert(arr[i] == 0); + arr[i] = (3 - i); + } +} + +/* ecall_array_in_out: + * arr[] will be allocated inside the enclave, content of arr[] will be copied either. + * After ECALL returns, the results will be copied to the outside. + */ +void ecall_array_in_out(int arr[4]) +{ + for (int i = 0; i < 4; i++) { + assert(arr[i] == i); + arr[i] = (3 - i); + } +} + +/* ecall_array_isary: + * [isary] tells Edger8r that user defined 'array_t' is an array type. + */ +void ecall_array_isary(array_t arr) +{ + if (sgx_is_outside_enclave(arr, sizeof(array_t)) != 1) + abort(); + + int n = sizeof(array_t)/sizeof(arr[0]); + for (int i = 0; i < n; i++) { + assert(arr[i] == i); + /* Below code performs as arr[i] = (n - 1 - i); + * It writes 4 bytes to untrusted memory, not 8 bytes aligned. + * So we need to use memcpy_verw() for security consideration. + * */ + int tmp = n -1 - i; + memcpy_verw(&arr[i], &tmp, sizeof(int)); + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.edl new file mode 100755 index 000000000..3d46c60f0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.edl @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Arrays.edl - Samples for array attributes. */ + +enclave { + + /* + * Only for fixed-size array (size is explicitly specified). + */ + + trusted { + + /* + * []: can be used to declare an array. + * [user_check]: + * pointer of the array won't be verified, and the buffer pointed by 'arr' + * is not copied into the enclave either. But enclave can modify the memory outside. + */ + + public void ecall_array_user_check([user_check] int arr[4]); + + /* + * [in]: + * buffer for the array will be allocated inside the enclave, + * content of the array will be copied into the new allocated memory inside. + * Any changes performed inside the enclave will not affect the array outside. + */ + + public void ecall_array_in([in] int arr[4]); + + /* + * [out]: + * buffer for the array will be allocated inside the enclave, + * but the content of the array won't be copied. After ECALL returns, + * the buffer inside the enclave will copied into outside array. + */ + + public void ecall_array_out([out] int arr[4]); + + /* + * [in, out]: + * buffer for the array will be allocated inside the enclave, + * the content of the array will be copied either. After ECALL returns, + * the buffer inside the enclave will by copied into outside array again. + */ + + public void ecall_array_in_out([in, out] int arr[4]); + + /* + * [isary]: + * tells Edger8r the user defined 'array_t' is an array type, 'arr' will be + * treated as a pointer, no memory copied either due to [user_check]. + * For OCALLs, 'arr' shall point to the memory outside the enclave. + */ + + public void ecall_array_isary([user_check, isary] array_t arr); + + }; + + untrusted { + + /* + * [user_check|in|out|in,out|isary] can also be used in OCALLs, refer to the "User Guide" for details. + */ + + }; + +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.o new file mode 100644 index 000000000..4228599c6 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Arrays.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.cpp new file mode 100755 index 000000000..c4091b601 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Test Calling Conventions */ + +#include +#include + +#include "../Enclave.h" +#include "Enclave_t.h" + +/* ecall_function_public: + * The public ECALL that invokes the OCALL 'ocall_function_allow'. + */ +void ecall_function_public(void) +{ + sgx_status_t ret = SGX_ERROR_UNEXPECTED; + + ret = ocall_function_allow(); + if (ret != SGX_SUCCESS) + abort(); + + return; +} + +/* ecall_function_private: + * The private ECALL that only can be invoked in the OCALL 'ocall_function_allow'. + */ +int ecall_function_private(void) +{ + return 1; +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.edl new file mode 100755 index 000000000..2c85d199d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.edl @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Functions.edl - Samples for function attributes. */ + +enclave { + + /* + * Following keywords/attributes are supported for untrusted functions: + * cdecl, stdcall, fastcall, dllimport (only for Windows). + * [public] is only supported for the trusted functions. + * Trusted function will be treated as [private] w/o the [public]. + */ + + trusted { + + /* + * [public]: + * public ECALL can be called directly in App. + */ + + public void ecall_function_public(void); + + /* + * [private]: + * private ECALL cannot be called directly in App. + */ + + int ecall_function_private(void); + + }; + + untrusted { + + /* + * [allow]: + * OCALL 'ocall_function_allow' can invoke ECALL 'ecall_function_private' in App side. + * + * Note: No ECALL can be called in OCALL w/o [allow]. + */ + + void ocall_function_allow(void) allow(ecall_function_private); + + }; + +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.o new file mode 100644 index 000000000..d341d79d5 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Functions.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.cpp new file mode 100755 index 000000000..ec60b1ede --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Test Pointer Auttributes */ + +#include +#include + +#include "../Enclave.h" +#include "Enclave_t.h" +#include "sgx_lfence.h" +#include "sgx_trts.h" + +/* checksum_internal: + * get simple checksum of input buffer and length + */ +int32_t checksum_internal(char* buf, size_t count) +{ + register int32_t sum = 0; + int16_t* ptr = (int16_t*)buf; + + /* Main summing loop */ + while (count > 1) { + sum = sum + *ptr++; + count = count - 2; + } + + /* Add left-over byte, if any */ + if (count > 0) { + sum = sum + *((char*)ptr); + } + + return ~sum; +} + +/* ecall_pointer_user_check, ecall_pointer_in, ecall_pointer_out, ecall_pointer_in_out: + * The root ECALLs to test [in], [out], [user_check] attributes. + */ +size_t ecall_pointer_user_check(void* val, size_t sz) +{ + /* check if the buffer is allocated outside */ + if (sgx_is_outside_enclave(val, sz) != 1) + abort(); + + /*fence after sgx_is_outside_enclave check*/ + sgx_lfence(); + + char tmp[100] = { 0 }; + size_t len = sz > 100 ? 100 : sz; + + /* copy the memory into the enclave to make sure 'val' + * is not being changed in checksum_internal() */ + memcpy(tmp, val, len); + + int32_t sum = checksum_internal((char*)tmp, len); + printf("Checksum(0x%p, %zu) = 0x%x\n", + val, len, (unsigned int)sum); + + /* modify outside memory directly */ + memcpy_verw(val, "SGX_SUCCESS", len > 12 ? 12 : len); + + return len; +} + +/* ecall_pointer_in: + * the buffer of val is copied to the enclave. + */ + +void ecall_pointer_in(int* val) +{ + if (sgx_is_within_enclave(val, sizeof(int)) != 1) + abort(); + assert(*val == 1); + *val = 1234; +} + +/* ecall_pointer_out: + * the buffer of val is copied to the untrusted side. + */ +void ecall_pointer_out(int* val) +{ + if (sgx_is_within_enclave(val, sizeof(int)) != 1) + abort(); + assert(*val == 0); + *val = 1234; +} + +/* ecall_pointer_in_out: + * the buffer of val is double-copied. + */ +void ecall_pointer_in_out(int* val) +{ + if (sgx_is_within_enclave(val, sizeof(int)) != 1) + abort(); + assert(*val == 1); + *val = 1234; +} + +/* ocall_pointer_attr: + * The root ECALL that test OCALL [in], [out], [user_check]. + */ +void ocall_pointer_attr(void) +{ + sgx_status_t ret = SGX_ERROR_UNEXPECTED; + + int val = 0; + ret = ocall_pointer_user_check(&val); + if (ret != SGX_SUCCESS) + abort(); + + val = 0; + ret = ocall_pointer_in(&val); + if (ret != SGX_SUCCESS) + abort(); + assert(val == 0); + + val = 0; + ret = ocall_pointer_out(&val); + if (ret != SGX_SUCCESS) + abort(); + assert(val == 1234); + + val = 0; + ret = ocall_pointer_in_out(&val); + if (ret != SGX_SUCCESS) + abort(); + assert(val == 1234); + + return; +} + +/* ecall_pointer_string: + * [string] defines a string. + */ +void ecall_pointer_string(char* str) +{ + strncpy(str, "0987654321", strlen(str)); +} + +/* ecall_pointer_string_const: + * const [string] defines a string that cannot be modified. + */ +void ecall_pointer_string_const(const char* str) +{ + char* temp = new char[strlen(str)]; + strncpy(temp, str, strlen(str)); + delete[] temp; +} + +/* ecall_pointer_size: + * 'len' needs to be specified to tell Edger8r the length of 'str'. + */ +void ecall_pointer_size(void* ptr, size_t len) +{ + strncpy((char*)ptr, "0987654321", len); +} + +/* ecall_pointer_count: + * 'cnt' needs to be specified to tell Edger8r the number of elements in 'arr'. + */ +void ecall_pointer_count(int* arr, size_t count) +{ + int cnt = (int)count; + for (int i = (cnt - 1); i >= 0; i--) + arr[i] = (cnt - 1 - i); +} + +/* ecall_pointer_isptr_readonly: + * 'buf' is user defined type, shall be tagged with [isptr]. + * if it's not writable, [readonly] shall be specified. + */ +void ecall_pointer_isptr_readonly(buffer_t buf, size_t len) +{ + strncpy((char*)buf, "0987654321", len); +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.edl new file mode 100755 index 000000000..54da69637 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.edl @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Pointers.edl - Samples for pointer attributes. */ + +enclave { + + /* + * Following keywords/attributes are supported for pointers in Edger8r: + * in, out, user_check, + * string, wstring, + * const, size, count, isptr, readonly + */ + + trusted { + + /* + * [user_check]: + * the pointer won't be validated, and the buffer pointed by + * 'val' is not copied into the enclave either. But Enclave + * can modify the memory pointed by 'val'. + */ + + public size_t ecall_pointer_user_check([user_check] void *val, size_t sz); + + /* + * [in]: + * buffer with the same size will be allocated inside the enclave, + * content pointed by 'val' will be copied into the new allocated + * memory inside. Any changes performed inside the enclave will not + * affect the buffer outside. + */ + + public void ecall_pointer_in([in] int *val); + + /* + * [out]: + * buffer with the same size will be allocated inside the enclave, + * but the content pointed by 'val' won't be copied. But after return, + * the buffer inside the enclave will copied into outside 'val'. + */ + + public void ecall_pointer_out([out] int *val); + + /* + * [in, out]: + * buffer with the same size will be allocated inside the enclave, + * the content pointed by 'val' will be copied either. After return, + * the buffer inside the enclave will by copied into outside 'val' again. + */ + + public void ecall_pointer_in_out([in, out] int *val); + + /* + * [string]: + * the attribute tells Edger8r 'str' is NULL terminated string, so strlen + * will be used to count the length of buffer pointed by 'str'. + */ + + public void ecall_pointer_string([in, out, string] char *str); + + /* + * [const]: + * the attribute tells Edger8r the buffer pointed by 'str' cannot be modified, + * so users cannot decorate 'str' with [out] attribute anymore. + */ + + public void ecall_pointer_string_const([in, string] const char *str); + + /* + * [size]: + * the attribute tells Edger8r the length of buffer in byte pointed by 'ptr' + * (shall be copied or not). + * Note: Users shall not specify [size] on [string] parameters. + */ + + public void ecall_pointer_size([in, out, size=len] void *ptr, size_t len); + + /* + * [count]: + * the attribute tells Edger8r the number of integers to be copied from 'arr'. + */ + + public void ecall_pointer_count([in, out, count=cnt] int *arr, size_t cnt); + + /* + * [isptr]: + * tells Edger8r the user defined type is a pointer; + * [readonly]: + * forbids the buffer allocated inside the enclave to be copied back to App + * (cannot use with [out]). + */ + + public void ecall_pointer_isptr_readonly([in, isptr, readonly, size=len] buffer_t buf, size_t len); + + }; + + /* + * Users can define multiple trusted/untrusted blocks, + * edger8r will merged them into one trusted/untrusted block. + */ + trusted { + /* + * Test pointer attributes in OCALLs + */ + + public void ocall_pointer_attr(void); + + }; + + untrusted { + + /* + * [user_check]: + * the pointer won't be verified, and the buffer pointed by 'val' is not + * copied to outside buffer either. Besides 'App' cannot modify the memory + * pointer by 'val'. + */ + + void ocall_pointer_user_check([user_check] int *val); + + /* + * [in]: + * buffer with the same size will be allocated in 'App' side, the content + * pointed by 'val' will be copied into the new allocated memory outside. + * Any changes performed by 'App' will not affect the buffer pointed by 'val'. + */ + + void ocall_pointer_in([in] int *val); + + /* + * [out]: + * buffer with the same size will be allocated in 'App' side, the content + * pointed by 'val' won't be copied. But after return, the buffer outside + * will be copied into the enclave. + */ + + void ocall_pointer_out([out] int *val); + + /* + * [in, out]: + * buffer with the same size will be allocated in 'App' side, the content + * pointed by 'val' will be copied either. After return, the buffer outside + * will copied into the enclave. + */ + + void ocall_pointer_in_out([in, out] int *val); + + }; + +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.o new file mode 100644 index 000000000..790961212 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Pointers.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.cpp new file mode 100755 index 000000000..3fd612397 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Test Basic Types */ + +#include "sgx_trts.h" +#include "../Enclave.h" +#include "Enclave_t.h" +#include +#include + +/* used to eliminate `unused variable' warning */ +#define UNUSED(val) (void)(val) + +#define ULP 2 + +/* used to compare double variables in order to avoid compile warnings */ +bool almost_equal(double x, double y) +{ + /* the machine epsilon has to be scaled to the magnitude of the larger value + and multiplied by the desired precision in ULPs (units in the last place) */ + return std::abs(x-y) <= std::numeric_limits::epsilon() * std::abs(x+y) * ULP; +} + +/* used to compare double variables in order to avoid compile warnings */ +bool almost_equal(float x, float y) +{ + /* the machine epsilon has to be scaled to the magnitude of the larger value + and multiplied by the desired precision in ULPs (units in the last place) */ + return std::abs(x-y) <= std::numeric_limits::epsilon() * std::abs(x+y) * ULP; +} + +/* ecall_type_char: + * [char] value passed by App. + */ +void ecall_type_char(char val) +{ + assert(val == 0x12); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* ecall_type_int: + * [int] value passed by App. + */ +void ecall_type_int(int val) +{ + assert(val == 1234); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* ecall_type_float: + * [float] value passed by App. + */ +void ecall_type_float(float val) +{ + assert(almost_equal(val, (float)1234.0)); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* ecall_type_double: + * [double] value passed by App. + */ +void ecall_type_double(double val) +{ + assert(almost_equal(val, (double)1234.5678)); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* ecall_type_size_t: + * [size_t] value passed by App. + */ +void ecall_type_size_t(size_t val) +{ + assert(val == (size_t)12345678); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* ecall_type_wchar_t: + * [wchar_t] value passed by App. + */ +void ecall_type_wchar_t(wchar_t val) +{ + assert(val == (wchar_t)0x1234); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* ecall_type_struct: + * struct_foo_t is defined in EDL and can be used in ECALL. + */ +void ecall_type_struct(struct struct_foo_t val) +{ + assert(val.struct_foo_0 == 1234); + assert(val.struct_foo_1 == 5678); +#ifndef DEBUG + UNUSED(val); +#endif +} + +/* + * ecall_type_enum_union: + * enum_foo_t/union_foo_t is defined in EDL + * and can be used in ECALL. + */ +void ecall_type_enum_union(enum enum_foo_t val1, union union_foo_t *val2) +{ + if (sgx_is_outside_enclave(val2, sizeof(union union_foo_t)) != 1) + abort(); + val2->union_foo_0 = 1; + val2->union_foo_1 = 2; /* overwrite union_foo_0 */ + assert(val1 == ENUM_FOO_0); +#ifndef DEBUG + UNUSED(val1); +#endif +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.edl new file mode 100755 index 000000000..d6c8bc898 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.edl @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Types.edl - Samples for basic types. */ + +enclave { + + /* + * Following types can be supported in Edger8r: + * char, short, int, float, double, void, + * int8_t, int16_t, int32_t, int64_t, + * size_t, wchar_t, + * uint8_t, uint16_t, uint32_t, uint64_t, + * unsigned, struct, enum, union. + */ + + /* + * We will demo few types in ECALL functions, data + * types in OCALL functions can be handled either. + */ + + /* structure definition */ + struct struct_foo_t { + /* Basic types can be used in structure. */ + uint32_t struct_foo_0; + uint64_t struct_foo_1; + }; + + /* enum definition */ + enum enum_foo_t { + ENUM_FOO_0 = 0, + ENUM_FOO_1 = 1 + }; + + /* union definition */ + union union_foo_t { + uint32_t union_foo_0; + uint32_t union_foo_1; + uint64_t union_foo_3; + }; + + trusted { + + public void ecall_type_char(char val); + public void ecall_type_int(int val); + + public void ecall_type_float(float val); + public void ecall_type_double(double val); + + public void ecall_type_size_t(size_t val); + public void ecall_type_wchar_t(wchar_t val); + + public void ecall_type_struct(struct struct_foo_t val); + public void ecall_type_enum_union(enum enum_foo_t val1, [user_check] union union_foo_t *val2); + + /* for using user defined types, please refer to Pointers.edl, Arrays.edl. */ + }; + +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.o new file mode 100644 index 000000000..6a70224ba Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Edger8rSyntax/Types.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.config.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.config.xml new file mode 100755 index 000000000..daee99867 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.config.xml @@ -0,0 +1,12 @@ + + 0 + 0 + 0x100000 + 0x200000 + 10 + 1 + + 0 + 0 + 0xFFFFFFFF + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.cpp new file mode 100755 index 000000000..42ef9bdc7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.cpp @@ -0,0 +1,866 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "Enclave.h" +#include "Enclave_t.h" /* print_string */ +#include +#include /* vsnprintf */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../common/shared_macros.h" + +/* + * printf: + * Invokes OCALL to display the enclave buffer to the terminal. + */ +int printf(const char* fmt, ...) +{ + char buf[BUFSIZ] = { '\0' }; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, BUFSIZ, fmt, ap); + va_end(ap); + ocall_print_string(buf); + return (int)strnlen(buf, BUFSIZ - 1) + 1; +} + +//-------------------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------------------- +// 辅助函数 + + +// // 将二进制字节数组逆序(大小端转换) +// void reverseByByte(unsigned char* param, size_t size) { +// for(size_t i = 0; i < size / 2; ++i) { +// unsigned char temp = param[i]; +// param[i] = param[size-i-1]; +// param[size-i-1] = temp; +// } +// } + +// 打印二进制字节数组的十六进制字符串 +void print_hex(const uint8_t* buffer, size_t size) { + for (size_t i = 0; i < size; i++) { + printf("%02x", buffer[i]); + // if ((i + 1) % 16 == 0) printf("\n"); + } + printf("\n"); +} + +// 将字节数组转换为十六进制字符串 +void biToHexString(const unsigned char* biArray, size_t biArraySize, char* outputHex) { + const char hex_digits[] = "0123456789abcdef"; // 十六进制字符映射表 + + for (size_t i = 0; i < biArraySize; ++i) { + unsigned char byte = biArray[i]; + // 通过位运算获取高4位和低4位的十六进制字符 + outputHex[i * 2] = hex_digits[byte >> 4]; // 高4位 + outputHex[i * 2 + 1] = hex_digits[byte & 0x0F]; // 低4位 + } + outputHex[biArraySize * 2] = '\0'; // 结束符 +} + +// 将十六进制字符串转换回字节数组 +bool hexStringToBi(const std::string& hex_str, unsigned char* array, size_t size) { + if (hex_str.length() != size * 2) { + printf("Error: imcompatible length: %d vs %d.\n", hex_str.length(), size * 2); + return false; // 十六进制字符串的长度必须是数组大小的两倍 + } + + for (size_t i = 0; i < size; ++i) { + unsigned int byte = 0; + // 解析每个字符对 + for (int j = 0; j < 2; ++j) { + char ch = hex_str[i * 2 + j]; + byte <<= 4; + if (ch >= '0' && ch <= '9') { + byte |= (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + byte |= (ch - 'a' + 10); + } else if (ch >= 'A' && ch <= 'F') { + byte |= (ch - 'A' + 10); + } else { + return false; // 如果遇到无效字符 + } + } + array[i] = static_cast(byte); + } + return true; +} + +// 将数据seal之后写入外部文件 +int seal_data_and_write_to_outer(const char* filename, unsigned char* buffer, size_t write_size, const int write_mode) { + // 1. Seal 当前数据(数据进行加密处理) + unsigned char sealed_data[MAX_SEAL_DATA_SIZE] = {0}; + uint32_t sealed_size = sgx_calc_sealed_data_size(0, write_size); // 获取密封后的数据大小 + // printf("Writing line: "); + // print_hex(buffer, write_size); + // printf("Sealed size: %d\n", sealed_size); + + sgx_status_t status = sgx_seal_data(0, nullptr, write_size, buffer, sealed_size, (sgx_sealed_data_t*)sealed_data); + if (status != SGX_SUCCESS) { + printf("Error: fail to seal data.\n"); + return -1; + } + + // 2. 将密封数据转换为十六进制字符串 + char hex_buffer[2 * MAX_SEAL_DATA_SIZE + 1] = {0}; // 存储十六进制字符串 + biToHexString(sealed_data, sealed_size, hex_buffer); // 转换密封后的数据为十六进制字符串 + + // 3. 添加换行符,确保写入的每次数据后都有换行符 + size_t len = strlen(hex_buffer); + hex_buffer[len] = '\n'; + hex_buffer[len + 1] = '\0'; // 添加换行符并确保字符串结尾 + + // 4. 调用外部函数(ocall)将密封后的十六进制字符串写入文件 + int ocall_write_ret = -1; + ocall_write_file(filename, hex_buffer, strlen(hex_buffer), write_mode, &ocall_write_ret); + if (ocall_write_ret != 0) { + printf("Error: fail to write sealed hex string to file.\n"); + return -1; + } + + return 0; +} + +// 读入外部文件数据并unseal +int read_from_outer_and_unseal(const char* filename, unsigned char* decrypted_buffer, size_t max_decrypted_buffer_size) { + // 1. 调用 ocall 读取文件(读取的是十六进制字符串) + char hex_buffer[MAX_FILE_SIZE] = {0}; // 临时存储从文件读取的十六进制字符串 + int ocall_read_ret = -1; + ocall_read_file(filename, hex_buffer, MAX_FILE_SIZE, &ocall_read_ret); + if (ocall_read_ret != 0) { + printf("Error: failed to read data from outer.\n"); + return -1; // 如果读取失败,返回 + } + + // 2. 将十六进制字符串逐行转换并解密 + std::istringstream hex_stream(hex_buffer); // 创建输入字符串流,按行读取 + std::string hex_line; // 存储每一行的十六进制数据 + unsigned char temp_decrypted_buffer[MAX_UNSEAL_DATA_SIZE] = {0}; // 临时解密后的数据 + size_t total_decrypted_size = 0; // 记录总的解密数据大小 + + while (std::getline(hex_stream, hex_line)) { + // printf("Sealed data(hex): %s\n", hex_line.c_str()); + // 每行的十六进制字符串转换为字节数组 + unsigned char sealed_data[MAX_SEAL_DATA_SIZE] = {0}; // 用于存放转换后的字节数组 + size_t sealed_data_size = hex_line.length() / 2; // 每个字节由两个字符表示 + + if (!hexStringToBi(hex_line, sealed_data, sealed_data_size)) { + printf("Error: failed to convert hex string to byte array for line: %s\n", hex_line.c_str()); + return -1; + } + + // 3. 解密(Unseal)操作 + size_t unsealed_data_size = MAX_UNSEAL_DATA_SIZE; + sgx_status_t status = sgx_unseal_data((sgx_sealed_data_t*)sealed_data, nullptr, nullptr, (unsigned char*)temp_decrypted_buffer, (uint32_t*)&unsealed_data_size); + if (status != SGX_SUCCESS) { + printf("Error: failed to unseal data\n"); + return -1; + } + + // printf("Unsealed size:%d, Unsealed line:", unsealed_data_size); + // print_hex(temp_decrypted_buffer, unsealed_data_size); + // 4. 检查解密后的数据大小是否超出最大缓冲区 + if (total_decrypted_size + unsealed_data_size > max_decrypted_buffer_size) { + printf("Error: not enough buffer space for unsealed data.\n"); + return -1; + } + + // 5. 将解密后的数据追加到输出缓冲区 + memcpy(decrypted_buffer + total_decrypted_size, temp_decrypted_buffer, unsealed_data_size); + total_decrypted_size += unsealed_data_size; + + memset(temp_decrypted_buffer, 0, sizeof(temp_decrypted_buffer)); + } + + // 7. 确保解密数据以null结尾 + if (total_decrypted_size < max_decrypted_buffer_size) { + decrypted_buffer[total_decrypted_size] = 0; + } else { + decrypted_buffer[max_decrypted_buffer_size - 1] = 0; + } + + return 0; +} + +// 验证两个读写函数的可行性 +// void ecall_test_enclave_function(int* ret) { +// char* msg = "Hello world\nHello SGX\nHello Dubley"; +// seal_data_and_write_to_outer("test.txt", (unsigned char*)("Hello world"), strlen("Hello world"), APPEND_MODE); +// seal_data_and_write_to_outer("test.txt", (unsigned char*)("Hello SGX"), strlen("Hello SGX"), APPEND_MODE); +// seal_data_and_write_to_outer("test.txt", (unsigned char*)("Hello Dubley"), strlen("Hello Dubley"), APPEND_MODE); +// char unseal_msg[MAX_UNSEAL_DATA_SIZE] = {0}; +// read_from_outer_and_unseal("test.txt", (unsigned char*)unseal_msg, sizeof(unseal_msg)); +// *ret = strcmp(msg, unseal_msg); +// } + +//-------------------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------------------- +// 交易异常监测部分 + + +// 交易预警逻辑函数 +int transaction_warning(int from_id, int to_id, double amount) { + if(amount > MAX_SINGLE_AMOUNT) { + return EXCESSIVE_SINGLE_AMOUNT; + } + return TRANSACTION_NO_EXCEPTION; +} + + +//-------------------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------------------- +// 密钥的生成、验证、存取和使用部分 + + +// 向外部写入密钥参数(十六进制字符串) +int write_rsa_key_params_to_file(const std::string filename, unsigned char* e, unsigned char* p_n, unsigned char* p_d, + unsigned char* p_p, unsigned char* p_q, unsigned char* p_dmp1, unsigned char* p_dmq1, unsigned char* p_iqmp) { + + int ret = seal_data_and_write_to_outer(filename.c_str(), e, 4, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_n, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_d, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_p, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_q, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_dmp1, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_dmq1, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + ret = seal_data_and_write_to_outer(filename.c_str(), p_iqmp, RSA_KEY_SIZE, APPEND_MODE); + if (ret != 0) { + return -1; + } + + return 0; +} + +// 从外部读入密钥参数并转换为二进制数组 +int read_rsa_key_params(const std::string filename, unsigned char* e, unsigned char* p_n, unsigned char* p_d, + unsigned char* p_p, unsigned char* p_q, unsigned char* p_dmp1, unsigned char* p_dmq1, unsigned char* p_iqmp) { + + unsigned char buffer[MAX_FILE_SIZE]; // 用于存储读取的文件内容 + + // 通过 OCALL 读取文件内容 + int read_rsa_params_ret = read_from_outer_and_unseal(filename.c_str(), buffer, MAX_FILE_SIZE); + if (read_rsa_params_ret != 0) { + printf("Failed to read wallet file: %s.\n", filename.c_str()); + return -1; + } + + // 每个参数的大小 + unsigned char* params[] = {e, p_n, p_d, p_p, p_q, p_dmp1, p_dmq1, p_iqmp}; + size_t param_sizes[] = {4, RSA_KEY_SIZE, RSA_KEY_SIZE, RSA_KEY_SIZE, RSA_KEY_SIZE, RSA_KEY_SIZE, RSA_KEY_SIZE, RSA_KEY_SIZE}; + + size_t offset = 0; + int param_index = 0; + + // 按照参数的字节数读取数据 + while (param_index < 8 && offset < MAX_FILE_SIZE) { + size_t line_length = param_sizes[param_index]; // 当前参数的字节大小 + + // 检查剩余的数据是否足够读取 + if (offset + line_length > MAX_FILE_SIZE) { + printf("Error: Not enough data in file to read parameter %d.\n", param_index); + return -1; + } + + // 将 buffer 中的当前参数数据复制到对应的参数 + memcpy(params[param_index], &buffer[offset], line_length); + + // 更新偏移量,跳过当前参数的字节数 + offset += line_length; + + // 处理下一个参数 + param_index++; + } + + // 检查是否读取了足够的参数 + if (param_index != 8) { + printf("Error: Incomplete data in file %s. Expected 8 parameters, but found %d.\n", filename.c_str(), param_index); + return -1; + } + + printf("RSA key params loaded successfully from file %s.\n", filename.c_str()); + return 0; +} + +// 从文件中重塑密钥对 +int restore_key_pair_from_file(const std::string filename, void** pub_key, void** priv_key) { + void *new_pub_key = NULL; + void *new_priv_key = NULL; + unsigned char e[4] = {0x01, 0x00, 0x01, 0x00}; + unsigned char p_n[RSA_KEY_SIZE] = {0}; + unsigned char p_d[RSA_KEY_SIZE] = {0}; + unsigned char p_p[RSA_KEY_SIZE] = {0}; + unsigned char p_q[RSA_KEY_SIZE] = {0}; + unsigned char p_dmp1[RSA_KEY_SIZE] = {0}; + unsigned char p_dmq1[RSA_KEY_SIZE] = {0}; + unsigned char p_iqmp[RSA_KEY_SIZE] = {0}; + + if(read_rsa_key_params(filename, e, p_n, p_d, p_p, p_q, p_dmp1, p_dmq1, p_iqmp) != 0){ + printf("Error: failed to read RSA key params from file.\n"); + return -1; + } + + sgx_status_t ret_create_private_key = sgx_create_rsa_priv1_key(RSA_KEY_SIZE, sizeof(e), RSA_KEY_SIZE, p_n, e, p_d, &new_priv_key); + if ( ret_create_private_key != SGX_SUCCESS) { + printf("Error: private key generation failed (%d)\n", ret_create_private_key); + return -1; + } + printf("Private key restored!\n"); + + sgx_status_t ret_create_public_key = sgx_create_rsa_pub1_key(RSA_KEY_SIZE, sizeof(e), p_n, e, &new_pub_key); + if ( ret_create_public_key != SGX_SUCCESS) { + printf("Error: public key generation failed (%d)\n", ret_create_public_key); + return -1; + } + printf("Public key restored!\n"); + + *pub_key = new_pub_key; + *priv_key = new_priv_key; + return 0; +} + +// 测试公私钥的配对性 +int test_key_pair_validation(void *public_key, void *private_key) { + // 原始明文 + const char* plaintext = "Hello, this is a secret message!"; + size_t plaintext_len = strlen(plaintext) + 1; // 包括结尾的 '\0' + + unsigned char encrypted_data[MAX_CIPHERTEXT_SIZE]; + size_t encrypted_size = MAX_CIPHERTEXT_SIZE; + + // 加密:使用公钥加密数据 + sgx_status_t status = sgx_rsa_pub_encrypt_sha256( + public_key, // 公钥 + encrypted_data, // 加密后的密文 + &encrypted_size, // 密文的大小 + (unsigned char*)plaintext, // 明文数据 + plaintext_len // 明文长度 + ); + + if (status != SGX_SUCCESS) { + printf("RSA encryption failed!\n"); + return -1; + } + + unsigned char decrypted_data[MAX_PLAINTEXT_SIZE]; + size_t decrypted_size = MAX_PLAINTEXT_SIZE; + + // 解密:使用私钥解密数据 + status = sgx_rsa_priv_decrypt_sha256( + private_key, // 私钥 + decrypted_data, // 解密后的明文数据 + &decrypted_size, // 解密后的数据长度 + encrypted_data, // 加密后的密文 + encrypted_size // 密文长度 + ); + + if (status != SGX_SUCCESS) { + printf("RSA decryption failed!\n"); + return -1; + } + + return strcmp((char*)decrypted_data, plaintext); +} + +// 测试指定文件的密钥参数是否可重塑 +int test_stored_rsa_params(const std::string filename, void *origin_pub_key, void* origin_priv_key) { + void *restored_pub_key = NULL; + void *restored_priv_key = NULL; + restore_key_pair_from_file(filename, &restored_pub_key, &restored_priv_key); + + int pass_cnt = 0; + pass_cnt += (test_key_pair_validation(origin_pub_key, origin_priv_key) == 0); + pass_cnt += (test_key_pair_validation(restored_pub_key, restored_priv_key) == 0); + pass_cnt += (test_key_pair_validation(restored_pub_key, origin_priv_key) == 0); + pass_cnt += (test_key_pair_validation(origin_pub_key, restored_priv_key) == 0); + + printf("Restored key pair from file passed %d/4 test.\n", pass_cnt); + if(pass_cnt != 4) { + return -1; + } + return 0; +} + +// 为指定用户生成密钥对 +int generate_new_rsa_key_pair(int wallet_id, unsigned char* ret_pub_key, unsigned char* ret_priv_key) { + void *g_public_key = NULL; + void *g_private_key = NULL; + unsigned char e[4] = {0x01, 0x00, 0x01, 0x00}; + unsigned char p_n[RSA_KEY_SIZE] = {0}; + unsigned char p_d[RSA_KEY_SIZE] = {0}; + unsigned char p_p[RSA_KEY_SIZE] = {0}; + unsigned char p_q[RSA_KEY_SIZE] = {0}; + unsigned char p_dmp1[RSA_KEY_SIZE] = {0}; + unsigned char p_dmq1[RSA_KEY_SIZE] = {0}; + unsigned char p_iqmp[RSA_KEY_SIZE] = {0}; + + sgx_status_t ret_create_key_params = sgx_create_rsa_key_pair(RSA_KEY_SIZE, sizeof(e), p_n, p_d, e, p_p, p_q, p_dmp1, p_dmq1, p_iqmp); + if (ret_create_key_params != SGX_SUCCESS) { + printf("Error: key params generation failed (%d)\n", ret_create_key_params); + return -1; + } + printf("Key params created!\n"); + + sgx_status_t ret_create_private_key = sgx_create_rsa_priv1_key(RSA_KEY_SIZE, sizeof(e), RSA_KEY_SIZE, p_n, e, p_d, &g_private_key); + if ( ret_create_private_key != SGX_SUCCESS) { + printf("Error: private key generation failed (%d)\n", ret_create_private_key); + return -1; + } + printf("Private key created!\n"); + + sgx_status_t ret_create_public_key = sgx_create_rsa_pub1_key(RSA_KEY_SIZE, sizeof(e), p_n, e, &g_public_key); + if ( ret_create_public_key != SGX_SUCCESS) { + printf("Error: public key generation failed (%d)\n", ret_create_public_key); + return -1; + } + printf("Public key created!\n"); + + // 将密钥参数写入外部文件 + const std::string filename = "Wallets/wallet" + std::to_string(wallet_id) + ".txt"; + if(write_rsa_key_params_to_file(filename, e, p_n, p_d, p_p, p_q, p_dmp1, p_dmq1, p_iqmp) != 0) { + printf("Error: failed to write RSA key params to file.\n"); + return -1; + } + printf("Successfully write rsa key params into file.\n"); + + if (test_stored_rsa_params(filename, g_public_key, g_private_key) != 0) { + printf("New rsa key pair failed the restoration test.\n"); + return -1; + } + + memcpy(ret_pub_key, p_n, RSA_KEY_SIZE); + memcpy(ret_priv_key, p_d, RSA_KEY_SIZE); + + return 0; +} + +// 使用用户公钥加密交易 +int encrypt_transaction_with_user_keys(int from_id, int to_id, double amount, char* encrypted_output, size_t encrypted_output_size) { + // 获取 from_id 和 to_id 的公私钥 + void* pub_key = NULL; + void* priv_key = NULL; + + const std::string filename = "Wallets/wallet" + std::to_string(from_id) + ".txt"; + if (restore_key_pair_from_file(filename, &pub_key, &priv_key) != 0) { + printf("Error: Failed to get RSA keys for wallet_id %d\n", from_id); + return -1; + } + + printf("Data to encrypt: \n", from_id, to_id, amount); + // 定义加密后的数据 + unsigned char encrypted_from_id[MAX_CIPHERTEXT_SIZE]; + unsigned char encrypted_to_id[MAX_CIPHERTEXT_SIZE]; + unsigned char encrypted_amount[MAX_CIPHERTEXT_SIZE]; + + size_t encrypted_from_id_size = MAX_CIPHERTEXT_SIZE; + size_t encrypted_to_id_size = MAX_CIPHERTEXT_SIZE; + size_t encrypted_amount_size = MAX_CIPHERTEXT_SIZE; + + // 加密 from_id + sgx_status_t status = sgx_rsa_pub_encrypt_sha256( + pub_key, // 公钥 + encrypted_from_id, // 加密后的 from_id + &encrypted_from_id_size,// 加密后的 from_id 的大小 + (unsigned char*)&from_id, // from_id 明文数据 + sizeof(from_id) // from_id 明文长度 + ); + if (status != SGX_SUCCESS) { + printf("Error: RSA encryption failed.\n"); + return -1; + } + + // 加密 to_id + status = sgx_rsa_pub_encrypt_sha256( + pub_key, // 公钥 + encrypted_to_id, // 加密后的 to_id + &encrypted_to_id_size, // 加密后的 to_id 的大小 + (unsigned char*)&to_id, // to_id 明文数据 + sizeof(to_id) // to_id 明文长度 + ); + if (status != SGX_SUCCESS) { + printf("Error: RSA encryption failed for to_id\n"); + return -1; + } + + // 加密 amount + status = sgx_rsa_pub_encrypt_sha256( + pub_key, // 公钥 + encrypted_amount, // 加密后的 amount + &encrypted_amount_size, // 加密后的 amount 的大小 + (unsigned char*)&amount, // amount 明文数据 + sizeof(amount) // amount 明文长度 + ); + if (status != SGX_SUCCESS) { + printf("Error: RSA encryption failed for amount\n"); + return -1; + } + + // 将加密后的数据转换为 16 进制字符串 + char encrypted_from_id_hex[MAX_CIPHERTEXT_SIZE * 2 + 1]; + char encrypted_to_id_hex[MAX_CIPHERTEXT_SIZE * 2 + 1]; + char encrypted_amount_hex[MAX_CIPHERTEXT_SIZE * 2 + 1]; + + biToHexString(encrypted_from_id, encrypted_from_id_size, encrypted_from_id_hex); + biToHexString(encrypted_to_id, encrypted_to_id_size, encrypted_to_id_hex); + biToHexString(encrypted_amount, encrypted_amount_size, encrypted_amount_hex); + + // 将格式化后的字符串存储到 encrypted_output + snprintf(encrypted_output, encrypted_output_size, + "from_id:%s,to_id:%s,amount:%s\n", + encrypted_from_id_hex, + encrypted_to_id_hex, + encrypted_amount_hex); + // printf("Length of encrypted string: %d\n", strlen(encrypted_output)); + return 0; +} + +// 使用Enclave私钥解密交易 +int decrypt_transaction_with_enclave_keys(const char* encrypted_from, const char* encrypted_to, const char* encrypted_amount, + size_t encrypted_size, int* from_id, int* to_id, double* amount) { + unsigned char from_bytes[RSA_KEY_SIZE] = {0}; + unsigned char to_bytes[RSA_KEY_SIZE] = {0}; + unsigned char amount_bytes[RSA_KEY_SIZE] = {0}; + + if (!hexStringToBi(encrypted_from, from_bytes, RSA_KEY_SIZE) || + !hexStringToBi(encrypted_to, to_bytes, RSA_KEY_SIZE) || + !hexStringToBi(encrypted_amount, amount_bytes, RSA_KEY_SIZE)) { + printf("Error: fail to convert the byte arrays into hex string.\n"); + return -1; + } + + // 恢复公钥和私钥 + void* pub_key = nullptr; + void* priv_key = nullptr; + std::string filename = "Enclave/enclave_keys.txt"; + if (restore_key_pair_from_file(filename, &pub_key, &priv_key) != 0) { + printf("Error: fail to restore key pairs of Enclave.\n"); + return -1; + } + + // 解密 "from" + unsigned char decrypted_from[MAX_PLAINTEXT_SIZE] = {0}; + size_t decrypted_from_size = MAX_PLAINTEXT_SIZE; + if (sgx_rsa_priv_decrypt_sha256(priv_key, decrypted_from, &decrypted_from_size, from_bytes, RSA_KEY_SIZE) != SGX_SUCCESS) { + printf("Error: fail to decrypt \"from\".\n"); + return -1; + } + + // 解密 "to" + unsigned char decrypted_to[MAX_PLAINTEXT_SIZE] = {0}; + size_t decrypted_to_size = MAX_PLAINTEXT_SIZE; + if (sgx_rsa_priv_decrypt_sha256(priv_key, decrypted_to, &decrypted_to_size, to_bytes, RSA_KEY_SIZE) != SGX_SUCCESS) { + printf("Error: fail to decrypt \"to\".\n"); + return -1; + } + + // 解密 "amount" + unsigned char decrypted_amount[MAX_PLAINTEXT_SIZE] = {0}; + size_t decrypted_amount_size = MAX_PLAINTEXT_SIZE; + if (sgx_rsa_priv_decrypt_sha256(priv_key, decrypted_amount, &decrypted_amount_size, amount_bytes, RSA_KEY_SIZE) != SGX_SUCCESS) { + printf("Error: fail to decrypt \"amount\".\n"); + return -1; + } + + *from_id = *(int*)decrypted_from; + *to_id = *(int*)decrypted_to; + *amount = *(double*)decrypted_amount; + + return 0;; // 解密成功 +} + + +//-------------------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------------------- +// 交易混洗部分 + + +// 用于生成随机浮点数的函数 [0.0, 1.0) +double generate_random_number() { + uint64_t random_int; // 64位整数,用于生成随机浮点数 + + // 调用 sgx_read_rand 生成 8 字节的随机数 + sgx_status_t status = sgx_read_rand((unsigned char*)&random_int, sizeof(random_int)); + + if (status == SGX_SUCCESS) { + // 归一化处理,将随机整数转换为 [0.0, 1.0) 范围的 double 值 + double random_value = (double)random_int / (double)UINT64_MAX; + return random_value; + } else { + printf("Fail to generate random number. Error code: %d\n", status); + return 0.0; // 返回 0.0 表示生成失败 + } +} + +// 随机选择代理钱包 +void get_random_wallets(int num_wallets, std::vector& selected_wallets) { + selected_wallets.clear(); // 清空已选择的钱包列表 + bool selected[POOL_SIZE + 1] = { false }; // 标记钱包是否已被选择 + selected[0] = true; + + while (selected_wallets.size() < num_wallets) { + int idx = (int)(generate_random_number() * POOL_SIZE); // 生成随机钱包索引 + + // 如果该钱包未被选择,标记为已选择并加入到 selected_wallets + if (!selected[idx]) { + selected_wallets.push_back(idx); + selected[idx] = true; // 标记为已选择 + } + } +} + +// 随机拆分交易金额 +void split_transaction_amount(double total_amount, int num_splits, std::vector& splits) { + double remaining_amount = total_amount; + + // 随机生成每个子交易的金额,并保留两位小数 + for (int i = 0; i < num_splits - 1; ++i) { + // 随机金额在剩余金额的 0 到 remaining_amount / 2 之间 + double max_amount = remaining_amount / 2; + double split_amount = generate_random_number() * max_amount; + + // 保留两位小数 + split_amount = (int)(split_amount * 100.0) / 100.0; + splits.push_back(split_amount); + remaining_amount -= split_amount; + } + + // 最后一个金额是剩余金额,保留两位小数 + splits.push_back((int)(remaining_amount * 100.0) / 100.0); +} + +// 实现交易混洗逻辑的函数 +void shuffle_transaction(int from_id, int to_id, double amount, char* shuffled_output, size_t shuffled_output_size) { + // 随机选择 3 到 6 个代理钱包 + int num_wallets = (int)(generate_random_number() * 4.0) + 3; // 随机选择 3 到 6 个钱包 + std::vector selected_wallets; + get_random_wallets(num_wallets, selected_wallets); + + // 将交易金额按比例拆分 + std::vector split_amounts; + split_transaction_amount(amount, num_wallets, split_amounts); + + // 输出拆分后的交易信息 + printf("Shuffling transaction: <"); + printf("From: %d To: %d Amount: %.2f>\n", from_id, to_id, amount); + for (int i = 0; i < num_wallets; ++i) { + printf("Address: %d Amount: %.2f\n", selected_wallets[i], split_amounts[i]); + } + + size_t offset = 0; + size_t vec_size = selected_wallets.size(); + for (size_t i = 0; i < vec_size; ++i) { + // 获取当前钱包地址 + int proxy_id = selected_wallets[i]; + char proxy_id_str[10]; + int len = snprintf(proxy_id_str, sizeof(proxy_id_str), "%d", proxy_id); // 获取钱包地址字符串长度 + + // 如果还有空间,将钱包地址复制到临时缓冲区 + if (offset + len < shuffled_output_size) { + memcpy(shuffled_output + offset, proxy_id_str, len); + offset += len; + } + + // 添加冒号分隔符 + if (offset + 1 < shuffled_output_size) { + shuffled_output[offset] = ':'; + offset++; + } + + // 获取拆分的金额 + double temp_amount = split_amounts[i]; + char amount_str[20] = {0}; + len = snprintf(amount_str, sizeof(amount_str), "%.2f", temp_amount); + + // 将金额字符串拷贝到缓冲区 + if (offset + len < shuffled_output_size) { + memcpy(shuffled_output + offset, amount_str, len); + offset += len; + } + + // 如果不是最后一个钱包地址,添加逗号 + if (i != vec_size - 1) { + if (offset + 1 < shuffled_output_size) { + shuffled_output[offset] = ','; + offset++; + } + } + } + // 确保字符数组以 null 结尾 + shuffled_output[offset] = '\0'; +} + + +//-------------------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------------------- +// ecall接口 + +// ecall测试函数 +void ecall_add(int a, int b, int *result) { + *result = a + b; +} + +// 创建一个新的钱包,并返回公钥和私钥 +void ecall_create_wallet(unsigned char* ret_pub_key, unsigned char* ret_priv_key, size_t pub_key_len, int wallet_id, int* ret){ + // 构造文件名 + std::string filename = "Wallets/wallet" + std::to_string(wallet_id) + ".txt"; + + // 判断文件是否存在 + int file_exists = 0; + ocall_file_exists(filename.c_str(), &file_exists); + + if (file_exists == 1) { + *ret = -1; // 钱包文件已经存在 + return; + } + + printf("Generating key pair for wallet_id %d\n", wallet_id); + if(generate_new_rsa_key_pair(wallet_id, ret_pub_key, ret_priv_key) != 0){ + *ret = -2; //生成密钥对失败 + return; + } + + *ret = 0; +} + +// 混洗交易(并将交易原文存至本地) +void ecall_deal_transaction(int from_id, int to_id, double amount, char* shuffled_output, size_t shuffled_output_size, + char* encrypted_output, size_t encrypted_output_size, int* warning_sign, int* ret) { + *warning_sign = transaction_warning(from_id, to_id, amount); + + shuffle_transaction(from_id, to_id, amount, shuffled_output, shuffled_output_size); + printf("Transaction shuffled successfully.\n"); + + // if(encrypt_transaction_with_user_keys(from_id, to_id, amount, encrypted_output, encrypted_output_size) != 0) { + // printf("Failed to encrypt transaction.\n"); + // *ret = -1; + // return; + // } + // printf("Transaction data encrypted successfully.\n"); + ret = 0; +} + +// 用Enclave公钥解密交易数据,并用用户公钥加密后存至本地 +void ecall_decrypt_transaction_data(const char* encrypted_from, const char* encrypted_to, const char* encrypted_amount, + size_t encrypted_size, int* from_id, int* to_id, double* amount, int* ret) { + if(decrypt_transaction_with_enclave_keys(encrypted_from, encrypted_to, encrypted_amount, encrypted_size, from_id, to_id, amount) != 0) { + printf("Fail to decrypt trasaction data.\n"); + *ret = -1; + return; + } + printf("from: %d\nto: %d\namount%lf\nSuccessfully decrypt transaction.\n", *from_id, *to_id, *amount); + + char encrypted_output[MAX_TRANSACTION_ENCRYPTED_SIZE]; + size_t encrypted_output_size = MAX_TRANSACTION_ENCRYPTED_SIZE; + encrypt_transaction_with_user_keys(*from_id, *to_id, *amount, encrypted_output, encrypted_output_size); + const std::string filename = "Transactions/wallet" + std::to_string(*from_id) + ".txt"; + int write_data_out_ret = -1; + ocall_write_file(filename.c_str(), encrypted_output, encrypted_output_size, APPEND_MODE, &write_data_out_ret); + if(write_data_out_ret != 0) { + printf("Error: fail to write encrypted transaction into file.\n"); + *ret = -1; + return; + } + printf("Successfully write encrypted transaction into file.\n"); + + *ret = 0; +} + +// 用用户公钥加密交易数据,并将交易密文存至本地 +void ecall_encrypt_transaction(int from_id, int to_id, double amount, int* ret) { + char encrypted_output[MAX_TRANSACTION_ENCRYPTED_SIZE]; + size_t encrypted_output_size = MAX_TRANSACTION_ENCRYPTED_SIZE; + + if(encrypt_transaction_with_user_keys(from_id, to_id, amount, encrypted_output, encrypted_output_size) != 0) { + printf("Error: fail to encrypt trasaction.\n"); + *ret = -1; + return; + } + + printf("Succesfully encrypt transaction.\n"); + + const std::string filename = "Transactions/wallet" + std::to_string(from_id) + ".txt"; + int write_data_out_ret = -1; + ocall_write_file(filename.c_str(), encrypted_output, encrypted_output_size, APPEND_MODE, &write_data_out_ret); + if(write_data_out_ret != 0) { + printf("Error: fail to write encrypted transaction into file.\n"); + *ret = -1; + return; + } + printf("Successfully write encrypted transaction into file.\n"); + + *ret = 0; +} + +// 交易预警 +void ecall_transaction_warning(int from_id, int to_id, double amount, int* warning_sign, int* ret) { + *warning_sign = transaction_warning(from_id, to_id, amount); + *ret = 0; +} + + +//-------------------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------------------- diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.edl new file mode 100755 index 000000000..38bddced8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.edl @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Enclave.edl - Top EDL file. */ + +enclave { + + include "user_types.h" /* buffer_t */ + + /* Import ECALL/OCALL from sub-directory EDLs. + * [from]: specifies the location of EDL file. + * [import]: specifies the functions to import, + * [*]: implies to import all functions. + */ + + from "Edger8rSyntax/Types.edl" import *; + from "Edger8rSyntax/Pointers.edl" import *; + from "Edger8rSyntax/Arrays.edl" import *; + from "Edger8rSyntax/Functions.edl" import *; + + from "TrustedLibrary/Libc.edl" import *; + from "TrustedLibrary/Libcxx.edl" import ecall_exception, ecall_map; + from "TrustedLibrary/Thread.edl" import *; + + /* + * ocall_print_string - invokes OCALL to display string buffer inside the enclave. + * [in]: copy the string buffer to App outside. + * [string]: specifies 'str' is a NULL terminated buffer. + */ + trusted { + public void ecall_add(int a, int b, [out] int *result); + public void ecall_create_wallet([out, size=key_len] unsigned char* ret_pub_key, [out, size=key_len] unsigned char* ret_priv_key, + size_t key_len, int wallet_id, [out] int* ret); + public void ecall_deal_transaction(int from_id, int to_id, double amount, [out, size=shuffled_output_size] char* shuffled_output, size_t shuffled_output_size, + [out, size=encrypted_output_size] char* encrypted_output, size_t encrypted_output_size, [out] int* warning_sign, [out] int* ret); + public void ecall_decrypt_transaction_data([in, size=encrypted_size] const char* encrypted_from, [in, size=encrypted_size] const char* encrypted_id, + [in, size=encrypted_size] const char* encrypted_amount, size_t encrypted_size, + [out] int* from_id, [out] int* to_id, [out] double* amount,[out] int* ret); + public void ecall_encrypt_transaction(int from_id, int to_id, double amount, [out] int* ret); + public void ecall_transaction_warning(int from_id, int to_id, double amount, [out] int* warning_sign, [out] int* ret); + }; + + untrusted { + void ocall_print_string([in, string] const char *str); + void ocall_file_exists([in, string] const char* filename, [out] int* exists); + void ocall_read_file([in, string] const char* filename, [out, size=buffer_size] char* buffer, size_t buffer_size, [out] int* ret); + void ocall_write_file([in, string] const char* filename, [in, size=data_size] const char* data, size_t data_size, int write_mode, [out] int *ret); + }; + +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.h b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.h new file mode 100755 index 000000000..f6bb82b59 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ENCLAVE_H_ +#define _ENCLAVE_H_ + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int printf(const char* fmt, ...); + +#if defined(__cplusplus) +} +#endif + +#endif /* !_ENCLAVE_H_ */ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.lds b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.lds new file mode 100755 index 000000000..0d5614f55 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.lds @@ -0,0 +1,11 @@ +enclave.so +{ + global: + g_global_data_sim; + g_global_data; + enclave_entry; + g_peak_heap_used; + g_peak_rsrv_mem_committed; + local: + *; +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.o new file mode 100644 index 000000000..5c21d107b Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_private_test.pem b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_private_test.pem new file mode 100644 index 000000000..9beafca18 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_private_test.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEA4mUdGPjcUMcUEXRSRtfFBLjO31MuywlhfuVGVZ6tBrozE95y +ltibcuiM0nxVNfCxPf303Tf0Dejbf+CE7esYXKpMle+otHWtVTWf6r4RJnTm76VG +Lir2YDxO8bsFqLaK92gOxa9wNhMgzrXHoTxVqTsHLFq1UbjlEkzQW3EgvxxP+poG +8jGvOQDC0n7kV0T6oRzPCUY0w8jy06KjMndfTw9J87vGsKdqsABgWt91GYtN3Z8D +MpI5TCYPFlNvLEXku0CBinrAJPm+OSNAH6xIyRegpqdaBIa7lAeYHrb8XcVvzR8t +7V+/5TAFr/bGq+NBVqo3OLalTHhzmaxgc6+YE2jdcuRBakU7/LhnZ0fcYmnCad8s +3S33aRcD+LWF2MJP8s/ED6cPDTMZaiHNC7bkiLcsx8FZ092hYwKjhrQqeUqls+s8 +EKgiSM1KmPQOJm62x8sqoLYu3IZtBR1dYMr6OKRjkJyt9LGTVUaHzRVhLTWf08Ce +oZwgClOR6m7sBYR1AgEDAoIBgQCW7hNl+z2LL2K2TYwvOoNYezSU4h8yBkD/Q4Q5 +FHNZ0XdilExkkGeh8F3hqDjOoHYpU/iTeqKz8JJVQFienLrocYhj9Rsi+R44zmqc +frYZo0Sfw4Qex07q0t9L0gPFzwdPmrSDykrOt2s0eS/A0uPGJ1odkc42e0NhiIrn +oMB/aDVRvAShdnTQqyyMVJg6LfxraIoGLs3X20yNFxd2+j+KCjFNJ9nLGkcgAEA8 +lPi7sjPpFKzMYXuIGV9kN59y2UHmbZYqLYKAQjM17Gx3/0OsFDhbCcInd8rAb0ca +F0VwWuiB570n8lrth/RZWwVLSOBI+iZL3+r6R+UBKiodC7No8oC0s7JjP2VfNY8n +ec1cV8rrJy1/6xAmbSKtdJn/A8GzV2QcU610xaSnWR1DZzUA4h7sXmY7qL5MdJJ5 +XvCFCU9eqoGXYUu2R/v+s9y9koJts2r6iH2qxBuQQE0xne6mMAZ3OAqsG8rV6TfB +v9ae/CP7ncVO56d8AtB9BpiwfXMCgcEA9kXz8V56xueaMH/abextLwddf4ix++1h +HqndLIS+8aNajI7UnaEgPWeIWck2fqVOzZZwN7NnYhTK4oJzeKtPxSvZ0DmcRj/Y +UzXx3hiwUZ6X1s0BVao8OXbDQIMgCgTBI2ebObY4wETjZHXo1scgDHUsmrJO6A4n +MJCbKIqifIGrg9u1T/Vp4l/UN46a1MQxW70GWz5P56ofR1bgMD5IXnGYU0VSu4o/ +4mn/rSAmY382a9ejPcqZrjRvj1eeC517AoHBAOtWLFnYAZ2u1zfAwv3A9hfx7p6Q +BM1lqlS20EsPVUOZuH20vZPTF0N8jtAlCDxQohucjY8zXXL30TVqrc9yuzDRQpOd +GY8mS5qyHs34eAZHejJXZ0ejIvX8jLQDftA37EJlEqtzUh3FvwqmOE/U9Pru0cqB +cZJSXL/DLKgbHzU7AyIPxF2gxtUBfGNXqDVOwceBA82rInS2JGVmCWYfy+DqwYqD +Szb9ozH+tH1VeNs8M258U212CyIa6Z+NaPEqzwKBwQCkLqKg6acvRRF1qpGenZ4f +Wj5VBcv9SOtpxpNzAyn2bOcIXzhpFhV+RQWRMM7/GN8zuaAlIkTsDdyXAaJQcjUu +HTvgJmgu1TriI/aUEHWLvw/kiKuOcX17pIIrAhVcAyts77zRJCXVg0JC+UXkhMAI +Th28dt9FXsTLCxIbBxb9q8etPSOKo5vsP+LPtGc4gsuSfgQ81DVFHBTaOerK1DA+ +9mWM2OHSXCqW8VUeFW7s/3mdOmzT3GZ0IvUKOmldE6cCgcEAnOQdkTqrvnSPeoCB +/oCkD/afFGADM5kcOHngMgo417vQU83Tt+IPglMJ4BiwKDXBZ72ztMzo90/gzkce +ikx8yzYsYmi7tMQyZyFp3qWlWYT8IY+aL8IXTqhdzVep4CVILENhx6I2voPUscQl +io34p0nhMauhDDbof9dzGry/eNICFrUtk8CEjgD9l4/FeN8r2lYCiRzBoyQYQ5lb +mWqH60crsazcz1PCIVR4U45QkigiSag3nk6yFrybv7ObS3HfAoHBAIGKsgAQuGDN +mVOAaYr9fhFK9RQ9aHKIB8AowVI3BR9bFGo8l0ON5/7QUkJ2zLB5QN4oaIY73ySY +IEw78sMdOsWxK5QNRL0J26xqJYs7RivmouevpsKC2HeJBJ9l8QRs2GEfhxTP5WbJ +kLBfwlSnm0CBd4PVUrdhx+IdFMWWj7l2tZPk7RKYEsWlqVEVE0GlcvsMuEPI/SfX +Vh6g5Jnu3fdHGwZ6d7yoou/jUPQ4iFEc+uvQcHXRTiXbyR0tGzqLxQ== +-----END RSA PRIVATE KEY----- diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_public.pem b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_public.pem new file mode 100644 index 000000000..3fc09f382 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_public.pem @@ -0,0 +1,11 @@ +-----BEGIN PUBLIC KEY----- +MIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIBiAKCAYEAtCf99rhgAEsaSBtV7wVr +Njk222bO7+WTCXyl+bHDhtzKlMdWZ/eSzNd8vUt0j0iksb7r/kWFTAez81+KufeX +9MlRaF59wMZ/Q3h4KOMNjPzkk+XMJr5ejsqEg/tCXJ+vjdG8iACIorqHoB5M/Rge +NQcION+BjpLe1H8iS5M22mQptoHz0QFinSetnll9B6S0EUVkjHxXRdnmLJ9lLvYf +8lV17TIyeOHea+x9TpSCjEp78u1rgPKDN6dkUGx/kt97hjf7LnT12WOSK8xfh0hv +8Fh8N5pb8yHZgeDtp5k+9Rd1PUyp1f+LXhSgnEBfto1IaMtDOarsH1Vqvz+CUDjT +0AdMah53wyw1XRRKCGYOoOGAMnISnhzUEH8kjtXH/PBAZ+5J02rkfeCJk68p5ApJ +1kyPyvbCVBNUPA0Ie+l0kkewRhBcBrbduB0GZBGZM+hGcfsauQ7EwBR43q7fncJ/ +nb2inQrYDWP51Mvn9tTJnTM/KNJqkHIWjMukrGvgWaqRAgED +-----END PUBLIC KEY----- diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.c b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.c new file mode 100644 index 000000000..aad5d12c4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.c @@ -0,0 +1,2691 @@ +#include "Enclave_t.h" + +#include "sgx_trts.h" /* for sgx_ocalloc, sgx_is_outside_enclave */ +#include "sgx_lfence.h" /* for sgx_lfence */ + +#include +#include /* for memcpy_s etc */ +#include /* for malloc/free etc */ + +#define CHECK_REF_POINTER(ptr, siz) do { \ + if (!(ptr) || ! sgx_is_outside_enclave((ptr), (siz))) \ + return SGX_ERROR_INVALID_PARAMETER;\ +} while (0) + +#define CHECK_UNIQUE_POINTER(ptr, siz) do { \ + if ((ptr) && ! sgx_is_outside_enclave((ptr), (siz))) \ + return SGX_ERROR_INVALID_PARAMETER;\ +} while (0) + +#define CHECK_ENCLAVE_POINTER(ptr, siz) do { \ + if ((ptr) && ! sgx_is_within_enclave((ptr), (siz))) \ + return SGX_ERROR_INVALID_PARAMETER;\ +} while (0) + +#define ADD_ASSIGN_OVERFLOW(a, b) ( \ + ((a) += (b)) < (b) \ +) + + +typedef struct ms_ecall_add_t { + int ms_a; + int ms_b; + int* ms_result; +} ms_ecall_add_t; + +typedef struct ms_ecall_create_wallet_t { + unsigned char* ms_ret_pub_key; + unsigned char* ms_ret_priv_key; + size_t ms_key_len; + int ms_wallet_id; + int* ms_ret; +} ms_ecall_create_wallet_t; + +typedef struct ms_ecall_deal_transaction_t { + int ms_from_id; + int ms_to_id; + double ms_amount; + char* ms_shuffled_output; + size_t ms_shuffled_output_size; + char* ms_encrypted_output; + size_t ms_encrypted_output_size; + int* ms_warning_sign; + int* ms_ret; +} ms_ecall_deal_transaction_t; + +typedef struct ms_ecall_decrypt_transaction_data_t { + const char* ms_encrypted_from; + const char* ms_encrypted_id; + const char* ms_encrypted_amount; + size_t ms_encrypted_size; + int* ms_from_id; + int* ms_to_id; + double* ms_amount; + int* ms_ret; +} ms_ecall_decrypt_transaction_data_t; + +typedef struct ms_ecall_encrypt_transaction_t { + int ms_from_id; + int ms_to_id; + double ms_amount; + int* ms_ret; +} ms_ecall_encrypt_transaction_t; + +typedef struct ms_ecall_transaction_warning_t { + int ms_from_id; + int ms_to_id; + double ms_amount; + int* ms_warning_sign; + int* ms_ret; +} ms_ecall_transaction_warning_t; + +typedef struct ms_ecall_type_char_t { + char ms_val; +} ms_ecall_type_char_t; + +typedef struct ms_ecall_type_int_t { + int ms_val; +} ms_ecall_type_int_t; + +typedef struct ms_ecall_type_float_t { + float ms_val; +} ms_ecall_type_float_t; + +typedef struct ms_ecall_type_double_t { + double ms_val; +} ms_ecall_type_double_t; + +typedef struct ms_ecall_type_size_t_t { + size_t ms_val; +} ms_ecall_type_size_t_t; + +typedef struct ms_ecall_type_wchar_t_t { + wchar_t ms_val; +} ms_ecall_type_wchar_t_t; + +typedef struct ms_ecall_type_struct_t { + struct struct_foo_t ms_val; +} ms_ecall_type_struct_t; + +typedef struct ms_ecall_type_enum_union_t { + enum enum_foo_t ms_val1; + union union_foo_t* ms_val2; +} ms_ecall_type_enum_union_t; + +typedef struct ms_ecall_pointer_user_check_t { + size_t ms_retval; + void* ms_val; + size_t ms_sz; +} ms_ecall_pointer_user_check_t; + +typedef struct ms_ecall_pointer_in_t { + int* ms_val; +} ms_ecall_pointer_in_t; + +typedef struct ms_ecall_pointer_out_t { + int* ms_val; +} ms_ecall_pointer_out_t; + +typedef struct ms_ecall_pointer_in_out_t { + int* ms_val; +} ms_ecall_pointer_in_out_t; + +typedef struct ms_ecall_pointer_string_t { + char* ms_str; + size_t ms_str_len; +} ms_ecall_pointer_string_t; + +typedef struct ms_ecall_pointer_string_const_t { + const char* ms_str; + size_t ms_str_len; +} ms_ecall_pointer_string_const_t; + +typedef struct ms_ecall_pointer_size_t { + void* ms_ptr; + size_t ms_len; +} ms_ecall_pointer_size_t; + +typedef struct ms_ecall_pointer_count_t { + int* ms_arr; + size_t ms_cnt; +} ms_ecall_pointer_count_t; + +typedef struct ms_ecall_pointer_isptr_readonly_t { + buffer_t ms_buf; + size_t ms_len; +} ms_ecall_pointer_isptr_readonly_t; + +typedef struct ms_ecall_array_user_check_t { + int* ms_arr; +} ms_ecall_array_user_check_t; + +typedef struct ms_ecall_array_in_t { + int* ms_arr; +} ms_ecall_array_in_t; + +typedef struct ms_ecall_array_out_t { + int* ms_arr; +} ms_ecall_array_out_t; + +typedef struct ms_ecall_array_in_out_t { + int* ms_arr; +} ms_ecall_array_in_out_t; + +typedef struct ms_ecall_array_isary_t { + array_t* ms_arr; +} ms_ecall_array_isary_t; + +typedef struct ms_ecall_function_private_t { + int ms_retval; +} ms_ecall_function_private_t; + +typedef struct ms_ecall_sgx_cpuid_t { + int* ms_cpuinfo; + int ms_leaf; +} ms_ecall_sgx_cpuid_t; + +typedef struct ms_ecall_increase_counter_t { + size_t ms_retval; +} ms_ecall_increase_counter_t; + +typedef struct ms_ocall_print_string_t { + const char* ms_str; +} ms_ocall_print_string_t; + +typedef struct ms_ocall_file_exists_t { + const char* ms_filename; + int* ms_exists; +} ms_ocall_file_exists_t; + +typedef struct ms_ocall_read_file_t { + const char* ms_filename; + char* ms_buffer; + size_t ms_buffer_size; + int* ms_ret; +} ms_ocall_read_file_t; + +typedef struct ms_ocall_write_file_t { + const char* ms_filename; + const char* ms_data; + size_t ms_data_size; + int ms_write_mode; + int* ms_ret; +} ms_ocall_write_file_t; + +typedef struct ms_ocall_pointer_user_check_t { + int* ms_val; +} ms_ocall_pointer_user_check_t; + +typedef struct ms_ocall_pointer_in_t { + int* ms_val; +} ms_ocall_pointer_in_t; + +typedef struct ms_ocall_pointer_out_t { + int* ms_val; +} ms_ocall_pointer_out_t; + +typedef struct ms_ocall_pointer_in_out_t { + int* ms_val; +} ms_ocall_pointer_in_out_t; + +typedef struct ms_sgx_oc_cpuidex_t { + int* ms_cpuinfo; + int ms_leaf; + int ms_subleaf; +} ms_sgx_oc_cpuidex_t; + +typedef struct ms_sgx_thread_wait_untrusted_event_ocall_t { + int ms_retval; + const void* ms_self; +} ms_sgx_thread_wait_untrusted_event_ocall_t; + +typedef struct ms_sgx_thread_set_untrusted_event_ocall_t { + int ms_retval; + const void* ms_waiter; +} ms_sgx_thread_set_untrusted_event_ocall_t; + +typedef struct ms_sgx_thread_setwait_untrusted_events_ocall_t { + int ms_retval; + const void* ms_waiter; + const void* ms_self; +} ms_sgx_thread_setwait_untrusted_events_ocall_t; + +typedef struct ms_sgx_thread_set_multiple_untrusted_events_ocall_t { + int ms_retval; + const void** ms_waiters; + size_t ms_total; +} ms_sgx_thread_set_multiple_untrusted_events_ocall_t; + +static sgx_status_t SGX_CDECL sgx_ecall_add(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_add_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_add_t* ms = SGX_CAST(ms_ecall_add_t*, pms); + ms_ecall_add_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_add_t), ms, sizeof(ms_ecall_add_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_result = __in_ms.ms_result; + size_t _len_result = sizeof(int); + int* _in_result = NULL; + + CHECK_UNIQUE_POINTER(_tmp_result, _len_result); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_result != NULL && _len_result != 0) { + if ( _len_result % sizeof(*_tmp_result) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_result = (int*)malloc(_len_result)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_result, 0, _len_result); + } + ecall_add(__in_ms.ms_a, __in_ms.ms_b, _in_result); + if (_in_result) { + if (memcpy_verw_s(_tmp_result, _len_result, _in_result, _len_result)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_result) free(_in_result); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_create_wallet(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_create_wallet_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_create_wallet_t* ms = SGX_CAST(ms_ecall_create_wallet_t*, pms); + ms_ecall_create_wallet_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_create_wallet_t), ms, sizeof(ms_ecall_create_wallet_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + unsigned char* _tmp_ret_pub_key = __in_ms.ms_ret_pub_key; + size_t _tmp_key_len = __in_ms.ms_key_len; + size_t _len_ret_pub_key = _tmp_key_len; + unsigned char* _in_ret_pub_key = NULL; + unsigned char* _tmp_ret_priv_key = __in_ms.ms_ret_priv_key; + size_t _len_ret_priv_key = _tmp_key_len; + unsigned char* _in_ret_priv_key = NULL; + int* _tmp_ret = __in_ms.ms_ret; + size_t _len_ret = sizeof(int); + int* _in_ret = NULL; + + CHECK_UNIQUE_POINTER(_tmp_ret_pub_key, _len_ret_pub_key); + CHECK_UNIQUE_POINTER(_tmp_ret_priv_key, _len_ret_priv_key); + CHECK_UNIQUE_POINTER(_tmp_ret, _len_ret); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_ret_pub_key != NULL && _len_ret_pub_key != 0) { + if ( _len_ret_pub_key % sizeof(*_tmp_ret_pub_key) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret_pub_key = (unsigned char*)malloc(_len_ret_pub_key)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret_pub_key, 0, _len_ret_pub_key); + } + if (_tmp_ret_priv_key != NULL && _len_ret_priv_key != 0) { + if ( _len_ret_priv_key % sizeof(*_tmp_ret_priv_key) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret_priv_key = (unsigned char*)malloc(_len_ret_priv_key)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret_priv_key, 0, _len_ret_priv_key); + } + if (_tmp_ret != NULL && _len_ret != 0) { + if ( _len_ret % sizeof(*_tmp_ret) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret = (int*)malloc(_len_ret)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret, 0, _len_ret); + } + ecall_create_wallet(_in_ret_pub_key, _in_ret_priv_key, _tmp_key_len, __in_ms.ms_wallet_id, _in_ret); + if (_in_ret_pub_key) { + if (memcpy_verw_s(_tmp_ret_pub_key, _len_ret_pub_key, _in_ret_pub_key, _len_ret_pub_key)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_ret_priv_key) { + if (memcpy_verw_s(_tmp_ret_priv_key, _len_ret_priv_key, _in_ret_priv_key, _len_ret_priv_key)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_ret) { + if (memcpy_verw_s(_tmp_ret, _len_ret, _in_ret, _len_ret)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_ret_pub_key) free(_in_ret_pub_key); + if (_in_ret_priv_key) free(_in_ret_priv_key); + if (_in_ret) free(_in_ret); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_deal_transaction(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_deal_transaction_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_deal_transaction_t* ms = SGX_CAST(ms_ecall_deal_transaction_t*, pms); + ms_ecall_deal_transaction_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_deal_transaction_t), ms, sizeof(ms_ecall_deal_transaction_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + char* _tmp_shuffled_output = __in_ms.ms_shuffled_output; + size_t _tmp_shuffled_output_size = __in_ms.ms_shuffled_output_size; + size_t _len_shuffled_output = _tmp_shuffled_output_size; + char* _in_shuffled_output = NULL; + char* _tmp_encrypted_output = __in_ms.ms_encrypted_output; + size_t _tmp_encrypted_output_size = __in_ms.ms_encrypted_output_size; + size_t _len_encrypted_output = _tmp_encrypted_output_size; + char* _in_encrypted_output = NULL; + int* _tmp_warning_sign = __in_ms.ms_warning_sign; + size_t _len_warning_sign = sizeof(int); + int* _in_warning_sign = NULL; + int* _tmp_ret = __in_ms.ms_ret; + size_t _len_ret = sizeof(int); + int* _in_ret = NULL; + + CHECK_UNIQUE_POINTER(_tmp_shuffled_output, _len_shuffled_output); + CHECK_UNIQUE_POINTER(_tmp_encrypted_output, _len_encrypted_output); + CHECK_UNIQUE_POINTER(_tmp_warning_sign, _len_warning_sign); + CHECK_UNIQUE_POINTER(_tmp_ret, _len_ret); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_shuffled_output != NULL && _len_shuffled_output != 0) { + if ( _len_shuffled_output % sizeof(*_tmp_shuffled_output) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_shuffled_output = (char*)malloc(_len_shuffled_output)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_shuffled_output, 0, _len_shuffled_output); + } + if (_tmp_encrypted_output != NULL && _len_encrypted_output != 0) { + if ( _len_encrypted_output % sizeof(*_tmp_encrypted_output) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_encrypted_output = (char*)malloc(_len_encrypted_output)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_encrypted_output, 0, _len_encrypted_output); + } + if (_tmp_warning_sign != NULL && _len_warning_sign != 0) { + if ( _len_warning_sign % sizeof(*_tmp_warning_sign) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_warning_sign = (int*)malloc(_len_warning_sign)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_warning_sign, 0, _len_warning_sign); + } + if (_tmp_ret != NULL && _len_ret != 0) { + if ( _len_ret % sizeof(*_tmp_ret) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret = (int*)malloc(_len_ret)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret, 0, _len_ret); + } + ecall_deal_transaction(__in_ms.ms_from_id, __in_ms.ms_to_id, __in_ms.ms_amount, _in_shuffled_output, _tmp_shuffled_output_size, _in_encrypted_output, _tmp_encrypted_output_size, _in_warning_sign, _in_ret); + if (_in_shuffled_output) { + if (memcpy_verw_s(_tmp_shuffled_output, _len_shuffled_output, _in_shuffled_output, _len_shuffled_output)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_encrypted_output) { + if (memcpy_verw_s(_tmp_encrypted_output, _len_encrypted_output, _in_encrypted_output, _len_encrypted_output)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_warning_sign) { + if (memcpy_verw_s(_tmp_warning_sign, _len_warning_sign, _in_warning_sign, _len_warning_sign)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_ret) { + if (memcpy_verw_s(_tmp_ret, _len_ret, _in_ret, _len_ret)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_shuffled_output) free(_in_shuffled_output); + if (_in_encrypted_output) free(_in_encrypted_output); + if (_in_warning_sign) free(_in_warning_sign); + if (_in_ret) free(_in_ret); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_decrypt_transaction_data(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_decrypt_transaction_data_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_decrypt_transaction_data_t* ms = SGX_CAST(ms_ecall_decrypt_transaction_data_t*, pms); + ms_ecall_decrypt_transaction_data_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_decrypt_transaction_data_t), ms, sizeof(ms_ecall_decrypt_transaction_data_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + const char* _tmp_encrypted_from = __in_ms.ms_encrypted_from; + size_t _tmp_encrypted_size = __in_ms.ms_encrypted_size; + size_t _len_encrypted_from = _tmp_encrypted_size; + char* _in_encrypted_from = NULL; + const char* _tmp_encrypted_id = __in_ms.ms_encrypted_id; + size_t _len_encrypted_id = _tmp_encrypted_size; + char* _in_encrypted_id = NULL; + const char* _tmp_encrypted_amount = __in_ms.ms_encrypted_amount; + size_t _len_encrypted_amount = _tmp_encrypted_size; + char* _in_encrypted_amount = NULL; + int* _tmp_from_id = __in_ms.ms_from_id; + size_t _len_from_id = sizeof(int); + int* _in_from_id = NULL; + int* _tmp_to_id = __in_ms.ms_to_id; + size_t _len_to_id = sizeof(int); + int* _in_to_id = NULL; + double* _tmp_amount = __in_ms.ms_amount; + size_t _len_amount = sizeof(double); + double* _in_amount = NULL; + int* _tmp_ret = __in_ms.ms_ret; + size_t _len_ret = sizeof(int); + int* _in_ret = NULL; + + CHECK_UNIQUE_POINTER(_tmp_encrypted_from, _len_encrypted_from); + CHECK_UNIQUE_POINTER(_tmp_encrypted_id, _len_encrypted_id); + CHECK_UNIQUE_POINTER(_tmp_encrypted_amount, _len_encrypted_amount); + CHECK_UNIQUE_POINTER(_tmp_from_id, _len_from_id); + CHECK_UNIQUE_POINTER(_tmp_to_id, _len_to_id); + CHECK_UNIQUE_POINTER(_tmp_amount, _len_amount); + CHECK_UNIQUE_POINTER(_tmp_ret, _len_ret); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_encrypted_from != NULL && _len_encrypted_from != 0) { + if ( _len_encrypted_from % sizeof(*_tmp_encrypted_from) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_encrypted_from = (char*)malloc(_len_encrypted_from); + if (_in_encrypted_from == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_encrypted_from, _len_encrypted_from, _tmp_encrypted_from, _len_encrypted_from)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + if (_tmp_encrypted_id != NULL && _len_encrypted_id != 0) { + if ( _len_encrypted_id % sizeof(*_tmp_encrypted_id) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_encrypted_id = (char*)malloc(_len_encrypted_id); + if (_in_encrypted_id == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_encrypted_id, _len_encrypted_id, _tmp_encrypted_id, _len_encrypted_id)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + if (_tmp_encrypted_amount != NULL && _len_encrypted_amount != 0) { + if ( _len_encrypted_amount % sizeof(*_tmp_encrypted_amount) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_encrypted_amount = (char*)malloc(_len_encrypted_amount); + if (_in_encrypted_amount == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_encrypted_amount, _len_encrypted_amount, _tmp_encrypted_amount, _len_encrypted_amount)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + if (_tmp_from_id != NULL && _len_from_id != 0) { + if ( _len_from_id % sizeof(*_tmp_from_id) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_from_id = (int*)malloc(_len_from_id)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_from_id, 0, _len_from_id); + } + if (_tmp_to_id != NULL && _len_to_id != 0) { + if ( _len_to_id % sizeof(*_tmp_to_id) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_to_id = (int*)malloc(_len_to_id)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_to_id, 0, _len_to_id); + } + if (_tmp_amount != NULL && _len_amount != 0) { + if ( _len_amount % sizeof(*_tmp_amount) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_amount = (double*)malloc(_len_amount)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_amount, 0, _len_amount); + } + if (_tmp_ret != NULL && _len_ret != 0) { + if ( _len_ret % sizeof(*_tmp_ret) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret = (int*)malloc(_len_ret)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret, 0, _len_ret); + } + ecall_decrypt_transaction_data((const char*)_in_encrypted_from, (const char*)_in_encrypted_id, (const char*)_in_encrypted_amount, _tmp_encrypted_size, _in_from_id, _in_to_id, _in_amount, _in_ret); + if (_in_from_id) { + if (memcpy_verw_s(_tmp_from_id, _len_from_id, _in_from_id, _len_from_id)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_to_id) { + if (memcpy_verw_s(_tmp_to_id, _len_to_id, _in_to_id, _len_to_id)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_amount) { + if (memcpy_verw_s(_tmp_amount, _len_amount, _in_amount, _len_amount)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_ret) { + if (memcpy_verw_s(_tmp_ret, _len_ret, _in_ret, _len_ret)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_encrypted_from) free(_in_encrypted_from); + if (_in_encrypted_id) free(_in_encrypted_id); + if (_in_encrypted_amount) free(_in_encrypted_amount); + if (_in_from_id) free(_in_from_id); + if (_in_to_id) free(_in_to_id); + if (_in_amount) free(_in_amount); + if (_in_ret) free(_in_ret); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_encrypt_transaction(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_encrypt_transaction_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_encrypt_transaction_t* ms = SGX_CAST(ms_ecall_encrypt_transaction_t*, pms); + ms_ecall_encrypt_transaction_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_encrypt_transaction_t), ms, sizeof(ms_ecall_encrypt_transaction_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_ret = __in_ms.ms_ret; + size_t _len_ret = sizeof(int); + int* _in_ret = NULL; + + CHECK_UNIQUE_POINTER(_tmp_ret, _len_ret); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_ret != NULL && _len_ret != 0) { + if ( _len_ret % sizeof(*_tmp_ret) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret = (int*)malloc(_len_ret)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret, 0, _len_ret); + } + ecall_encrypt_transaction(__in_ms.ms_from_id, __in_ms.ms_to_id, __in_ms.ms_amount, _in_ret); + if (_in_ret) { + if (memcpy_verw_s(_tmp_ret, _len_ret, _in_ret, _len_ret)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_ret) free(_in_ret); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_transaction_warning(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_transaction_warning_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_transaction_warning_t* ms = SGX_CAST(ms_ecall_transaction_warning_t*, pms); + ms_ecall_transaction_warning_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_transaction_warning_t), ms, sizeof(ms_ecall_transaction_warning_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_warning_sign = __in_ms.ms_warning_sign; + size_t _len_warning_sign = sizeof(int); + int* _in_warning_sign = NULL; + int* _tmp_ret = __in_ms.ms_ret; + size_t _len_ret = sizeof(int); + int* _in_ret = NULL; + + CHECK_UNIQUE_POINTER(_tmp_warning_sign, _len_warning_sign); + CHECK_UNIQUE_POINTER(_tmp_ret, _len_ret); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_warning_sign != NULL && _len_warning_sign != 0) { + if ( _len_warning_sign % sizeof(*_tmp_warning_sign) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_warning_sign = (int*)malloc(_len_warning_sign)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_warning_sign, 0, _len_warning_sign); + } + if (_tmp_ret != NULL && _len_ret != 0) { + if ( _len_ret % sizeof(*_tmp_ret) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_ret = (int*)malloc(_len_ret)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_ret, 0, _len_ret); + } + ecall_transaction_warning(__in_ms.ms_from_id, __in_ms.ms_to_id, __in_ms.ms_amount, _in_warning_sign, _in_ret); + if (_in_warning_sign) { + if (memcpy_verw_s(_tmp_warning_sign, _len_warning_sign, _in_warning_sign, _len_warning_sign)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + if (_in_ret) { + if (memcpy_verw_s(_tmp_ret, _len_ret, _in_ret, _len_ret)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_warning_sign) free(_in_warning_sign); + if (_in_ret) free(_in_ret); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_char(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_char_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_char_t* ms = SGX_CAST(ms_ecall_type_char_t*, pms); + ms_ecall_type_char_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_char_t), ms, sizeof(ms_ecall_type_char_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_char(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_int(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_int_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_int_t* ms = SGX_CAST(ms_ecall_type_int_t*, pms); + ms_ecall_type_int_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_int_t), ms, sizeof(ms_ecall_type_int_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_int(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_float(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_float_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_float_t* ms = SGX_CAST(ms_ecall_type_float_t*, pms); + ms_ecall_type_float_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_float_t), ms, sizeof(ms_ecall_type_float_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_float(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_double(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_double_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_double_t* ms = SGX_CAST(ms_ecall_type_double_t*, pms); + ms_ecall_type_double_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_double_t), ms, sizeof(ms_ecall_type_double_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_double(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_size_t(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_size_t_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_size_t_t* ms = SGX_CAST(ms_ecall_type_size_t_t*, pms); + ms_ecall_type_size_t_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_size_t_t), ms, sizeof(ms_ecall_type_size_t_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_size_t(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_wchar_t(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_wchar_t_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_wchar_t_t* ms = SGX_CAST(ms_ecall_type_wchar_t_t*, pms); + ms_ecall_type_wchar_t_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_wchar_t_t), ms, sizeof(ms_ecall_type_wchar_t_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_wchar_t(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_struct(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_struct_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_struct_t* ms = SGX_CAST(ms_ecall_type_struct_t*, pms); + ms_ecall_type_struct_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_struct_t), ms, sizeof(ms_ecall_type_struct_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_type_struct(__in_ms.ms_val); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_type_enum_union(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_enum_union_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_type_enum_union_t* ms = SGX_CAST(ms_ecall_type_enum_union_t*, pms); + ms_ecall_type_enum_union_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_type_enum_union_t), ms, sizeof(ms_ecall_type_enum_union_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + union union_foo_t* _tmp_val2 = __in_ms.ms_val2; + + + ecall_type_enum_union(__in_ms.ms_val1, _tmp_val2); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_user_check(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_user_check_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_user_check_t* ms = SGX_CAST(ms_ecall_pointer_user_check_t*, pms); + ms_ecall_pointer_user_check_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_user_check_t), ms, sizeof(ms_ecall_pointer_user_check_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + void* _tmp_val = __in_ms.ms_val; + size_t _in_retval; + + + _in_retval = ecall_pointer_user_check(_tmp_val, __in_ms.ms_sz); + if (memcpy_verw_s(&ms->ms_retval, sizeof(ms->ms_retval), &_in_retval, sizeof(_in_retval))) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + +err: + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_in(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_in_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_in_t* ms = SGX_CAST(ms_ecall_pointer_in_t*, pms); + ms_ecall_pointer_in_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_in_t), ms, sizeof(ms_ecall_pointer_in_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_val = __in_ms.ms_val; + size_t _len_val = sizeof(int); + int* _in_val = NULL; + + CHECK_UNIQUE_POINTER(_tmp_val, _len_val); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_val != NULL && _len_val != 0) { + if ( _len_val % sizeof(*_tmp_val) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_val = (int*)malloc(_len_val); + if (_in_val == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_val, _len_val, _tmp_val, _len_val)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_pointer_in(_in_val); + +err: + if (_in_val) free(_in_val); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_out(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_out_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_out_t* ms = SGX_CAST(ms_ecall_pointer_out_t*, pms); + ms_ecall_pointer_out_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_out_t), ms, sizeof(ms_ecall_pointer_out_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_val = __in_ms.ms_val; + size_t _len_val = sizeof(int); + int* _in_val = NULL; + + CHECK_UNIQUE_POINTER(_tmp_val, _len_val); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_val != NULL && _len_val != 0) { + if ( _len_val % sizeof(*_tmp_val) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_val = (int*)malloc(_len_val)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_val, 0, _len_val); + } + ecall_pointer_out(_in_val); + if (_in_val) { + if (memcpy_verw_s(_tmp_val, _len_val, _in_val, _len_val)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_val) free(_in_val); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_in_out(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_in_out_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_in_out_t* ms = SGX_CAST(ms_ecall_pointer_in_out_t*, pms); + ms_ecall_pointer_in_out_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_in_out_t), ms, sizeof(ms_ecall_pointer_in_out_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_val = __in_ms.ms_val; + size_t _len_val = sizeof(int); + int* _in_val = NULL; + + CHECK_UNIQUE_POINTER(_tmp_val, _len_val); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_val != NULL && _len_val != 0) { + if ( _len_val % sizeof(*_tmp_val) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_val = (int*)malloc(_len_val); + if (_in_val == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_val, _len_val, _tmp_val, _len_val)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_pointer_in_out(_in_val); + if (_in_val) { + if (memcpy_verw_s(_tmp_val, _len_val, _in_val, _len_val)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_val) free(_in_val); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_string(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_string_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_string_t* ms = SGX_CAST(ms_ecall_pointer_string_t*, pms); + ms_ecall_pointer_string_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_string_t), ms, sizeof(ms_ecall_pointer_string_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + char* _tmp_str = __in_ms.ms_str; + size_t _len_str = __in_ms.ms_str_len ; + char* _in_str = NULL; + + CHECK_UNIQUE_POINTER(_tmp_str, _len_str); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_str != NULL && _len_str != 0) { + _in_str = (char*)malloc(_len_str); + if (_in_str == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_str, _len_str, _tmp_str, _len_str)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + _in_str[_len_str - 1] = '\0'; + if (_len_str != strlen(_in_str) + 1) + { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + ecall_pointer_string(_in_str); + if (_in_str) + { + _in_str[_len_str - 1] = '\0'; + _len_str = strlen(_in_str) + 1; + if (memcpy_verw_s((void*)_tmp_str, _len_str, _in_str, _len_str)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_str) free(_in_str); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_string_const(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_string_const_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_string_const_t* ms = SGX_CAST(ms_ecall_pointer_string_const_t*, pms); + ms_ecall_pointer_string_const_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_string_const_t), ms, sizeof(ms_ecall_pointer_string_const_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + const char* _tmp_str = __in_ms.ms_str; + size_t _len_str = __in_ms.ms_str_len ; + char* _in_str = NULL; + + CHECK_UNIQUE_POINTER(_tmp_str, _len_str); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_str != NULL && _len_str != 0) { + _in_str = (char*)malloc(_len_str); + if (_in_str == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_str, _len_str, _tmp_str, _len_str)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + _in_str[_len_str - 1] = '\0'; + if (_len_str != strlen(_in_str) + 1) + { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + ecall_pointer_string_const((const char*)_in_str); + +err: + if (_in_str) free(_in_str); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_size(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_size_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_size_t* ms = SGX_CAST(ms_ecall_pointer_size_t*, pms); + ms_ecall_pointer_size_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_size_t), ms, sizeof(ms_ecall_pointer_size_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + void* _tmp_ptr = __in_ms.ms_ptr; + size_t _tmp_len = __in_ms.ms_len; + size_t _len_ptr = _tmp_len; + void* _in_ptr = NULL; + + CHECK_UNIQUE_POINTER(_tmp_ptr, _len_ptr); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_ptr != NULL && _len_ptr != 0) { + _in_ptr = (void*)malloc(_len_ptr); + if (_in_ptr == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_ptr, _len_ptr, _tmp_ptr, _len_ptr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_pointer_size(_in_ptr, _tmp_len); + if (_in_ptr) { + if (memcpy_verw_s(_tmp_ptr, _len_ptr, _in_ptr, _len_ptr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_ptr) free(_in_ptr); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_count(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_count_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_count_t* ms = SGX_CAST(ms_ecall_pointer_count_t*, pms); + ms_ecall_pointer_count_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_count_t), ms, sizeof(ms_ecall_pointer_count_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_arr = __in_ms.ms_arr; + size_t _tmp_cnt = __in_ms.ms_cnt; + size_t _len_arr = _tmp_cnt * sizeof(int); + int* _in_arr = NULL; + + if (sizeof(*_tmp_arr) != 0 && + (size_t)_tmp_cnt > (SIZE_MAX / sizeof(*_tmp_arr))) { + return SGX_ERROR_INVALID_PARAMETER; + } + + CHECK_UNIQUE_POINTER(_tmp_arr, _len_arr); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_arr != NULL && _len_arr != 0) { + if ( _len_arr % sizeof(*_tmp_arr) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_arr = (int*)malloc(_len_arr); + if (_in_arr == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_arr, _len_arr, _tmp_arr, _len_arr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_pointer_count(_in_arr, _tmp_cnt); + if (_in_arr) { + if (memcpy_verw_s(_tmp_arr, _len_arr, _in_arr, _len_arr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_arr) free(_in_arr); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_pointer_isptr_readonly(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_pointer_isptr_readonly_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_pointer_isptr_readonly_t* ms = SGX_CAST(ms_ecall_pointer_isptr_readonly_t*, pms); + ms_ecall_pointer_isptr_readonly_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_pointer_isptr_readonly_t), ms, sizeof(ms_ecall_pointer_isptr_readonly_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + buffer_t _tmp_buf = __in_ms.ms_buf; + size_t _tmp_len = __in_ms.ms_len; + size_t _len_buf = _tmp_len; + buffer_t _in_buf = NULL; + + CHECK_UNIQUE_POINTER(_tmp_buf, _len_buf); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_buf != NULL && _len_buf != 0) { + _in_buf = (buffer_t)malloc(_len_buf); + if (_in_buf == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s((void*)_in_buf, _len_buf, _tmp_buf, _len_buf)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_pointer_isptr_readonly(_in_buf, _tmp_len); + +err: + if (_in_buf) free((void*)_in_buf); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ocall_pointer_attr(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ocall_pointer_attr(); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_array_user_check(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_array_user_check_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_array_user_check_t* ms = SGX_CAST(ms_ecall_array_user_check_t*, pms); + ms_ecall_array_user_check_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_array_user_check_t), ms, sizeof(ms_ecall_array_user_check_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_arr = __in_ms.ms_arr; + + + ecall_array_user_check(_tmp_arr); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_array_in(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_array_in_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_array_in_t* ms = SGX_CAST(ms_ecall_array_in_t*, pms); + ms_ecall_array_in_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_array_in_t), ms, sizeof(ms_ecall_array_in_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_arr = __in_ms.ms_arr; + size_t _len_arr = 4 * sizeof(int); + int* _in_arr = NULL; + + CHECK_UNIQUE_POINTER(_tmp_arr, _len_arr); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_arr != NULL && _len_arr != 0) { + if ( _len_arr % sizeof(*_tmp_arr) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_arr = (int*)malloc(_len_arr); + if (_in_arr == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_arr, _len_arr, _tmp_arr, _len_arr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_array_in(_in_arr); + +err: + if (_in_arr) free(_in_arr); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_array_out(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_array_out_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_array_out_t* ms = SGX_CAST(ms_ecall_array_out_t*, pms); + ms_ecall_array_out_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_array_out_t), ms, sizeof(ms_ecall_array_out_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_arr = __in_ms.ms_arr; + size_t _len_arr = 4 * sizeof(int); + int* _in_arr = NULL; + + CHECK_UNIQUE_POINTER(_tmp_arr, _len_arr); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_arr != NULL && _len_arr != 0) { + if ( _len_arr % sizeof(*_tmp_arr) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_arr = (int*)malloc(_len_arr)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_arr, 0, _len_arr); + } + ecall_array_out(_in_arr); + if (_in_arr) { + if (memcpy_verw_s(_tmp_arr, _len_arr, _in_arr, _len_arr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_arr) free(_in_arr); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_array_in_out(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_array_in_out_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_array_in_out_t* ms = SGX_CAST(ms_ecall_array_in_out_t*, pms); + ms_ecall_array_in_out_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_array_in_out_t), ms, sizeof(ms_ecall_array_in_out_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_arr = __in_ms.ms_arr; + size_t _len_arr = 4 * sizeof(int); + int* _in_arr = NULL; + + CHECK_UNIQUE_POINTER(_tmp_arr, _len_arr); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_arr != NULL && _len_arr != 0) { + if ( _len_arr % sizeof(*_tmp_arr) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + _in_arr = (int*)malloc(_len_arr); + if (_in_arr == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + if (memcpy_s(_in_arr, _len_arr, _tmp_arr, _len_arr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + + } + ecall_array_in_out(_in_arr); + if (_in_arr) { + if (memcpy_verw_s(_tmp_arr, _len_arr, _in_arr, _len_arr)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_arr) free(_in_arr); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_array_isary(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_array_isary_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_array_isary_t* ms = SGX_CAST(ms_ecall_array_isary_t*, pms); + ms_ecall_array_isary_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_array_isary_t), ms, sizeof(ms_ecall_array_isary_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + + + ecall_array_isary((__in_ms.ms_arr != NULL) ? (*__in_ms.ms_arr) : NULL); + + + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_function_public(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ecall_function_public(); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_function_private(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_function_private_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_function_private_t* ms = SGX_CAST(ms_ecall_function_private_t*, pms); + ms_ecall_function_private_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_function_private_t), ms, sizeof(ms_ecall_function_private_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int _in_retval; + + + _in_retval = ecall_function_private(); + if (memcpy_verw_s(&ms->ms_retval, sizeof(ms->ms_retval), &_in_retval, sizeof(_in_retval))) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + +err: + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_malloc_free(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ecall_malloc_free(); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_sgx_cpuid(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_sgx_cpuid_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_sgx_cpuid_t* ms = SGX_CAST(ms_ecall_sgx_cpuid_t*, pms); + ms_ecall_sgx_cpuid_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_sgx_cpuid_t), ms, sizeof(ms_ecall_sgx_cpuid_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + int* _tmp_cpuinfo = __in_ms.ms_cpuinfo; + size_t _len_cpuinfo = 4 * sizeof(int); + int* _in_cpuinfo = NULL; + + CHECK_UNIQUE_POINTER(_tmp_cpuinfo, _len_cpuinfo); + + // + // fence after pointer checks + // + sgx_lfence(); + + if (_tmp_cpuinfo != NULL && _len_cpuinfo != 0) { + if ( _len_cpuinfo % sizeof(*_tmp_cpuinfo) != 0) + { + status = SGX_ERROR_INVALID_PARAMETER; + goto err; + } + if ((_in_cpuinfo = (int*)malloc(_len_cpuinfo)) == NULL) { + status = SGX_ERROR_OUT_OF_MEMORY; + goto err; + } + + memset((void*)_in_cpuinfo, 0, _len_cpuinfo); + } + ecall_sgx_cpuid(_in_cpuinfo, __in_ms.ms_leaf); + if (_in_cpuinfo) { + if (memcpy_verw_s(_tmp_cpuinfo, _len_cpuinfo, _in_cpuinfo, _len_cpuinfo)) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + } + +err: + if (_in_cpuinfo) free(_in_cpuinfo); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_exception(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ecall_exception(); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_map(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ecall_map(); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_increase_counter(void* pms) +{ + CHECK_REF_POINTER(pms, sizeof(ms_ecall_increase_counter_t)); + // + // fence after pointer checks + // + sgx_lfence(); + ms_ecall_increase_counter_t* ms = SGX_CAST(ms_ecall_increase_counter_t*, pms); + ms_ecall_increase_counter_t __in_ms; + if (memcpy_s(&__in_ms, sizeof(ms_ecall_increase_counter_t), ms, sizeof(ms_ecall_increase_counter_t))) { + return SGX_ERROR_UNEXPECTED; + } + sgx_status_t status = SGX_SUCCESS; + size_t _in_retval; + + + _in_retval = ecall_increase_counter(); + if (memcpy_verw_s(&ms->ms_retval, sizeof(ms->ms_retval), &_in_retval, sizeof(_in_retval))) { + status = SGX_ERROR_UNEXPECTED; + goto err; + } + +err: + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_producer(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ecall_producer(); + return status; +} + +static sgx_status_t SGX_CDECL sgx_ecall_consumer(void* pms) +{ + sgx_status_t status = SGX_SUCCESS; + if (pms != NULL) return SGX_ERROR_INVALID_PARAMETER; + ecall_consumer(); + return status; +} + +SGX_EXTERNC const struct { + size_t nr_ecall; + struct {void* ecall_addr; uint8_t is_priv; uint8_t is_switchless;} ecall_table[38]; +} g_ecall_table = { + 38, + { + {(void*)(uintptr_t)sgx_ecall_add, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_create_wallet, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_deal_transaction, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_decrypt_transaction_data, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_encrypt_transaction, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_transaction_warning, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_char, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_int, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_float, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_double, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_size_t, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_wchar_t, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_struct, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_type_enum_union, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_user_check, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_in, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_out, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_in_out, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_string, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_string_const, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_size, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_count, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_pointer_isptr_readonly, 0, 0}, + {(void*)(uintptr_t)sgx_ocall_pointer_attr, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_array_user_check, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_array_in, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_array_out, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_array_in_out, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_array_isary, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_function_public, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_function_private, 1, 0}, + {(void*)(uintptr_t)sgx_ecall_malloc_free, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_sgx_cpuid, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_exception, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_map, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_increase_counter, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_producer, 0, 0}, + {(void*)(uintptr_t)sgx_ecall_consumer, 0, 0}, + } +}; + +SGX_EXTERNC const struct { + size_t nr_ocall; + uint8_t entry_table[14][38]; +} g_dyn_entry_table = { + 14, + { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + } +}; + + +sgx_status_t SGX_CDECL ocall_print_string(const char* str) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_str = str ? strlen(str) + 1 : 0; + + ms_ocall_print_string_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_print_string_t); + void *__tmp = NULL; + + + CHECK_ENCLAVE_POINTER(str, _len_str); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (str != NULL) ? _len_str : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_print_string_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_print_string_t)); + ocalloc_size -= sizeof(ms_ocall_print_string_t); + + if (str != NULL) { + if (memcpy_verw_s(&ms->ms_str, sizeof(const char*), &__tmp, sizeof(const char*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_str % sizeof(*str) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, str, _len_str)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_str); + ocalloc_size -= _len_str; + } else { + ms->ms_str = NULL; + } + + status = sgx_ocall(0, ms); + + if (status == SGX_SUCCESS) { + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_file_exists(const char* filename, int* exists) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_filename = filename ? strlen(filename) + 1 : 0; + size_t _len_exists = sizeof(int); + + ms_ocall_file_exists_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_file_exists_t); + void *__tmp = NULL; + + void *__tmp_exists = NULL; + + CHECK_ENCLAVE_POINTER(filename, _len_filename); + CHECK_ENCLAVE_POINTER(exists, _len_exists); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (filename != NULL) ? _len_filename : 0)) + return SGX_ERROR_INVALID_PARAMETER; + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (exists != NULL) ? _len_exists : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_file_exists_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_file_exists_t)); + ocalloc_size -= sizeof(ms_ocall_file_exists_t); + + if (filename != NULL) { + if (memcpy_verw_s(&ms->ms_filename, sizeof(const char*), &__tmp, sizeof(const char*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_filename % sizeof(*filename) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, filename, _len_filename)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_filename); + ocalloc_size -= _len_filename; + } else { + ms->ms_filename = NULL; + } + + if (exists != NULL) { + if (memcpy_verw_s(&ms->ms_exists, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_exists = __tmp; + if (_len_exists % sizeof(*exists) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + memset_verw(__tmp_exists, 0, _len_exists); + __tmp = (void *)((size_t)__tmp + _len_exists); + ocalloc_size -= _len_exists; + } else { + ms->ms_exists = NULL; + } + + status = sgx_ocall(1, ms); + + if (status == SGX_SUCCESS) { + if (exists) { + if (memcpy_s((void*)exists, _len_exists, __tmp_exists, _len_exists)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_read_file(const char* filename, char* buffer, size_t buffer_size, int* ret) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_filename = filename ? strlen(filename) + 1 : 0; + size_t _len_buffer = buffer_size; + size_t _len_ret = sizeof(int); + + ms_ocall_read_file_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_read_file_t); + void *__tmp = NULL; + + void *__tmp_buffer = NULL; + void *__tmp_ret = NULL; + + CHECK_ENCLAVE_POINTER(filename, _len_filename); + CHECK_ENCLAVE_POINTER(buffer, _len_buffer); + CHECK_ENCLAVE_POINTER(ret, _len_ret); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (filename != NULL) ? _len_filename : 0)) + return SGX_ERROR_INVALID_PARAMETER; + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (buffer != NULL) ? _len_buffer : 0)) + return SGX_ERROR_INVALID_PARAMETER; + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (ret != NULL) ? _len_ret : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_read_file_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_read_file_t)); + ocalloc_size -= sizeof(ms_ocall_read_file_t); + + if (filename != NULL) { + if (memcpy_verw_s(&ms->ms_filename, sizeof(const char*), &__tmp, sizeof(const char*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_filename % sizeof(*filename) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, filename, _len_filename)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_filename); + ocalloc_size -= _len_filename; + } else { + ms->ms_filename = NULL; + } + + if (buffer != NULL) { + if (memcpy_verw_s(&ms->ms_buffer, sizeof(char*), &__tmp, sizeof(char*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_buffer = __tmp; + if (_len_buffer % sizeof(*buffer) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + memset_verw(__tmp_buffer, 0, _len_buffer); + __tmp = (void *)((size_t)__tmp + _len_buffer); + ocalloc_size -= _len_buffer; + } else { + ms->ms_buffer = NULL; + } + + if (memcpy_verw_s(&ms->ms_buffer_size, sizeof(ms->ms_buffer_size), &buffer_size, sizeof(buffer_size))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + if (ret != NULL) { + if (memcpy_verw_s(&ms->ms_ret, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_ret = __tmp; + if (_len_ret % sizeof(*ret) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + memset_verw(__tmp_ret, 0, _len_ret); + __tmp = (void *)((size_t)__tmp + _len_ret); + ocalloc_size -= _len_ret; + } else { + ms->ms_ret = NULL; + } + + status = sgx_ocall(2, ms); + + if (status == SGX_SUCCESS) { + if (buffer) { + if (memcpy_s((void*)buffer, _len_buffer, __tmp_buffer, _len_buffer)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + if (ret) { + if (memcpy_s((void*)ret, _len_ret, __tmp_ret, _len_ret)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_write_file(const char* filename, const char* data, size_t data_size, int write_mode, int* ret) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_filename = filename ? strlen(filename) + 1 : 0; + size_t _len_data = data_size; + size_t _len_ret = sizeof(int); + + ms_ocall_write_file_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_write_file_t); + void *__tmp = NULL; + + void *__tmp_ret = NULL; + + CHECK_ENCLAVE_POINTER(filename, _len_filename); + CHECK_ENCLAVE_POINTER(data, _len_data); + CHECK_ENCLAVE_POINTER(ret, _len_ret); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (filename != NULL) ? _len_filename : 0)) + return SGX_ERROR_INVALID_PARAMETER; + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (data != NULL) ? _len_data : 0)) + return SGX_ERROR_INVALID_PARAMETER; + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (ret != NULL) ? _len_ret : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_write_file_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_write_file_t)); + ocalloc_size -= sizeof(ms_ocall_write_file_t); + + if (filename != NULL) { + if (memcpy_verw_s(&ms->ms_filename, sizeof(const char*), &__tmp, sizeof(const char*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_filename % sizeof(*filename) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, filename, _len_filename)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_filename); + ocalloc_size -= _len_filename; + } else { + ms->ms_filename = NULL; + } + + if (data != NULL) { + if (memcpy_verw_s(&ms->ms_data, sizeof(const char*), &__tmp, sizeof(const char*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_data % sizeof(*data) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, data, _len_data)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_data); + ocalloc_size -= _len_data; + } else { + ms->ms_data = NULL; + } + + if (memcpy_verw_s(&ms->ms_data_size, sizeof(ms->ms_data_size), &data_size, sizeof(data_size))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + if (memcpy_verw_s(&ms->ms_write_mode, sizeof(ms->ms_write_mode), &write_mode, sizeof(write_mode))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + if (ret != NULL) { + if (memcpy_verw_s(&ms->ms_ret, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_ret = __tmp; + if (_len_ret % sizeof(*ret) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + memset_verw(__tmp_ret, 0, _len_ret); + __tmp = (void *)((size_t)__tmp + _len_ret); + ocalloc_size -= _len_ret; + } else { + ms->ms_ret = NULL; + } + + status = sgx_ocall(3, ms); + + if (status == SGX_SUCCESS) { + if (ret) { + if (memcpy_s((void*)ret, _len_ret, __tmp_ret, _len_ret)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_pointer_user_check(int* val) +{ + sgx_status_t status = SGX_SUCCESS; + + ms_ocall_pointer_user_check_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_pointer_user_check_t); + void *__tmp = NULL; + + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_pointer_user_check_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_pointer_user_check_t)); + ocalloc_size -= sizeof(ms_ocall_pointer_user_check_t); + + if (memcpy_verw_s(&ms->ms_val, sizeof(ms->ms_val), &val, sizeof(val))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + status = sgx_ocall(4, ms); + + if (status == SGX_SUCCESS) { + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_pointer_in(int* val) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_val = sizeof(int); + + ms_ocall_pointer_in_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_pointer_in_t); + void *__tmp = NULL; + + + CHECK_ENCLAVE_POINTER(val, _len_val); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (val != NULL) ? _len_val : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_pointer_in_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_pointer_in_t)); + ocalloc_size -= sizeof(ms_ocall_pointer_in_t); + + if (val != NULL) { + if (memcpy_verw_s(&ms->ms_val, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_val % sizeof(*val) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, val, _len_val)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_val); + ocalloc_size -= _len_val; + } else { + ms->ms_val = NULL; + } + + status = sgx_ocall(5, ms); + + if (status == SGX_SUCCESS) { + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_pointer_out(int* val) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_val = sizeof(int); + + ms_ocall_pointer_out_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_pointer_out_t); + void *__tmp = NULL; + + void *__tmp_val = NULL; + + CHECK_ENCLAVE_POINTER(val, _len_val); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (val != NULL) ? _len_val : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_pointer_out_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_pointer_out_t)); + ocalloc_size -= sizeof(ms_ocall_pointer_out_t); + + if (val != NULL) { + if (memcpy_verw_s(&ms->ms_val, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_val = __tmp; + if (_len_val % sizeof(*val) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + memset_verw(__tmp_val, 0, _len_val); + __tmp = (void *)((size_t)__tmp + _len_val); + ocalloc_size -= _len_val; + } else { + ms->ms_val = NULL; + } + + status = sgx_ocall(6, ms); + + if (status == SGX_SUCCESS) { + if (val) { + if (memcpy_s((void*)val, _len_val, __tmp_val, _len_val)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_pointer_in_out(int* val) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_val = sizeof(int); + + ms_ocall_pointer_in_out_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_ocall_pointer_in_out_t); + void *__tmp = NULL; + + void *__tmp_val = NULL; + + CHECK_ENCLAVE_POINTER(val, _len_val); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (val != NULL) ? _len_val : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_ocall_pointer_in_out_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_pointer_in_out_t)); + ocalloc_size -= sizeof(ms_ocall_pointer_in_out_t); + + if (val != NULL) { + if (memcpy_verw_s(&ms->ms_val, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_val = __tmp; + if (_len_val % sizeof(*val) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, val, _len_val)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_val); + ocalloc_size -= _len_val; + } else { + ms->ms_val = NULL; + } + + status = sgx_ocall(7, ms); + + if (status == SGX_SUCCESS) { + if (val) { + if (memcpy_s((void*)val, _len_val, __tmp_val, _len_val)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL ocall_function_allow(void) +{ + sgx_status_t status = SGX_SUCCESS; + status = sgx_ocall(8, NULL); + + return status; +} +sgx_status_t SGX_CDECL sgx_oc_cpuidex(int cpuinfo[4], int leaf, int subleaf) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_cpuinfo = 4 * sizeof(int); + + ms_sgx_oc_cpuidex_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_sgx_oc_cpuidex_t); + void *__tmp = NULL; + + void *__tmp_cpuinfo = NULL; + + CHECK_ENCLAVE_POINTER(cpuinfo, _len_cpuinfo); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (cpuinfo != NULL) ? _len_cpuinfo : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_sgx_oc_cpuidex_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_oc_cpuidex_t)); + ocalloc_size -= sizeof(ms_sgx_oc_cpuidex_t); + + if (cpuinfo != NULL) { + if (memcpy_verw_s(&ms->ms_cpuinfo, sizeof(int*), &__tmp, sizeof(int*))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp_cpuinfo = __tmp; + if (_len_cpuinfo % sizeof(*cpuinfo) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + memset_verw(__tmp_cpuinfo, 0, _len_cpuinfo); + __tmp = (void *)((size_t)__tmp + _len_cpuinfo); + ocalloc_size -= _len_cpuinfo; + } else { + ms->ms_cpuinfo = NULL; + } + + if (memcpy_verw_s(&ms->ms_leaf, sizeof(ms->ms_leaf), &leaf, sizeof(leaf))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + if (memcpy_verw_s(&ms->ms_subleaf, sizeof(ms->ms_subleaf), &subleaf, sizeof(subleaf))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + status = sgx_ocall(9, ms); + + if (status == SGX_SUCCESS) { + if (cpuinfo) { + if (memcpy_s((void*)cpuinfo, _len_cpuinfo, __tmp_cpuinfo, _len_cpuinfo)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL sgx_thread_wait_untrusted_event_ocall(int* retval, const void* self) +{ + sgx_status_t status = SGX_SUCCESS; + + ms_sgx_thread_wait_untrusted_event_ocall_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_sgx_thread_wait_untrusted_event_ocall_t); + void *__tmp = NULL; + + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_sgx_thread_wait_untrusted_event_ocall_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_wait_untrusted_event_ocall_t)); + ocalloc_size -= sizeof(ms_sgx_thread_wait_untrusted_event_ocall_t); + + if (memcpy_verw_s(&ms->ms_self, sizeof(ms->ms_self), &self, sizeof(self))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + status = sgx_ocall(10, ms); + + if (status == SGX_SUCCESS) { + if (retval) { + if (memcpy_s((void*)retval, sizeof(*retval), &ms->ms_retval, sizeof(ms->ms_retval))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL sgx_thread_set_untrusted_event_ocall(int* retval, const void* waiter) +{ + sgx_status_t status = SGX_SUCCESS; + + ms_sgx_thread_set_untrusted_event_ocall_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_sgx_thread_set_untrusted_event_ocall_t); + void *__tmp = NULL; + + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_sgx_thread_set_untrusted_event_ocall_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_set_untrusted_event_ocall_t)); + ocalloc_size -= sizeof(ms_sgx_thread_set_untrusted_event_ocall_t); + + if (memcpy_verw_s(&ms->ms_waiter, sizeof(ms->ms_waiter), &waiter, sizeof(waiter))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + status = sgx_ocall(11, ms); + + if (status == SGX_SUCCESS) { + if (retval) { + if (memcpy_s((void*)retval, sizeof(*retval), &ms->ms_retval, sizeof(ms->ms_retval))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL sgx_thread_setwait_untrusted_events_ocall(int* retval, const void* waiter, const void* self) +{ + sgx_status_t status = SGX_SUCCESS; + + ms_sgx_thread_setwait_untrusted_events_ocall_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_sgx_thread_setwait_untrusted_events_ocall_t); + void *__tmp = NULL; + + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_sgx_thread_setwait_untrusted_events_ocall_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_setwait_untrusted_events_ocall_t)); + ocalloc_size -= sizeof(ms_sgx_thread_setwait_untrusted_events_ocall_t); + + if (memcpy_verw_s(&ms->ms_waiter, sizeof(ms->ms_waiter), &waiter, sizeof(waiter))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + if (memcpy_verw_s(&ms->ms_self, sizeof(ms->ms_self), &self, sizeof(self))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + status = sgx_ocall(12, ms); + + if (status == SGX_SUCCESS) { + if (retval) { + if (memcpy_s((void*)retval, sizeof(*retval), &ms->ms_retval, sizeof(ms->ms_retval))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + +sgx_status_t SGX_CDECL sgx_thread_set_multiple_untrusted_events_ocall(int* retval, const void** waiters, size_t total) +{ + sgx_status_t status = SGX_SUCCESS; + size_t _len_waiters = total * sizeof(void*); + + ms_sgx_thread_set_multiple_untrusted_events_ocall_t* ms = NULL; + size_t ocalloc_size = sizeof(ms_sgx_thread_set_multiple_untrusted_events_ocall_t); + void *__tmp = NULL; + + + CHECK_ENCLAVE_POINTER(waiters, _len_waiters); + + if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (waiters != NULL) ? _len_waiters : 0)) + return SGX_ERROR_INVALID_PARAMETER; + + __tmp = sgx_ocalloc(ocalloc_size); + if (__tmp == NULL) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + ms = (ms_sgx_thread_set_multiple_untrusted_events_ocall_t*)__tmp; + __tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_set_multiple_untrusted_events_ocall_t)); + ocalloc_size -= sizeof(ms_sgx_thread_set_multiple_untrusted_events_ocall_t); + + if (waiters != NULL) { + if (memcpy_verw_s(&ms->ms_waiters, sizeof(const void**), &__tmp, sizeof(const void**))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + if (_len_waiters % sizeof(*waiters) != 0) { + sgx_ocfree(); + return SGX_ERROR_INVALID_PARAMETER; + } + if (memcpy_verw_s(__tmp, ocalloc_size, waiters, _len_waiters)) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + __tmp = (void *)((size_t)__tmp + _len_waiters); + ocalloc_size -= _len_waiters; + } else { + ms->ms_waiters = NULL; + } + + if (memcpy_verw_s(&ms->ms_total, sizeof(ms->ms_total), &total, sizeof(total))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + + status = sgx_ocall(13, ms); + + if (status == SGX_SUCCESS) { + if (retval) { + if (memcpy_s((void*)retval, sizeof(*retval), &ms->ms_retval, sizeof(ms->ms_retval))) { + sgx_ocfree(); + return SGX_ERROR_UNEXPECTED; + } + } + } + sgx_ocfree(); + return status; +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.h b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.h new file mode 100644 index 000000000..1b6e8ed47 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.h @@ -0,0 +1,99 @@ +#ifndef ENCLAVE_T_H__ +#define ENCLAVE_T_H__ + +#include +#include +#include +#include "sgx_edger8r.h" /* for sgx_ocall etc. */ + +#include "user_types.h" + +#include /* for size_t */ + +#define SGX_CAST(type, item) ((type)(item)) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _struct_foo_t +#define _struct_foo_t +typedef struct struct_foo_t { + uint32_t struct_foo_0; + uint64_t struct_foo_1; +} struct_foo_t; +#endif + +typedef enum enum_foo_t { + ENUM_FOO_0 = 0, + ENUM_FOO_1 = 1, +} enum_foo_t; + +#ifndef _union_foo_t +#define _union_foo_t +typedef union union_foo_t { + uint32_t union_foo_0; + uint32_t union_foo_1; + uint64_t union_foo_3; +} union_foo_t; +#endif + +void ecall_add(int a, int b, int* result); +void ecall_create_wallet(unsigned char* ret_pub_key, unsigned char* ret_priv_key, size_t key_len, int wallet_id, int* ret); +void ecall_deal_transaction(int from_id, int to_id, double amount, char* shuffled_output, size_t shuffled_output_size, char* encrypted_output, size_t encrypted_output_size, int* warning_sign, int* ret); +void ecall_decrypt_transaction_data(const char* encrypted_from, const char* encrypted_id, const char* encrypted_amount, size_t encrypted_size, int* from_id, int* to_id, double* amount, int* ret); +void ecall_encrypt_transaction(int from_id, int to_id, double amount, int* ret); +void ecall_transaction_warning(int from_id, int to_id, double amount, int* warning_sign, int* ret); +void ecall_type_char(char val); +void ecall_type_int(int val); +void ecall_type_float(float val); +void ecall_type_double(double val); +void ecall_type_size_t(size_t val); +void ecall_type_wchar_t(wchar_t val); +void ecall_type_struct(struct struct_foo_t val); +void ecall_type_enum_union(enum enum_foo_t val1, union union_foo_t* val2); +size_t ecall_pointer_user_check(void* val, size_t sz); +void ecall_pointer_in(int* val); +void ecall_pointer_out(int* val); +void ecall_pointer_in_out(int* val); +void ecall_pointer_string(char* str); +void ecall_pointer_string_const(const char* str); +void ecall_pointer_size(void* ptr, size_t len); +void ecall_pointer_count(int* arr, size_t cnt); +void ecall_pointer_isptr_readonly(buffer_t buf, size_t len); +void ocall_pointer_attr(void); +void ecall_array_user_check(int arr[4]); +void ecall_array_in(int arr[4]); +void ecall_array_out(int arr[4]); +void ecall_array_in_out(int arr[4]); +void ecall_array_isary(array_t arr); +void ecall_function_public(void); +int ecall_function_private(void); +void ecall_malloc_free(void); +void ecall_sgx_cpuid(int cpuinfo[4], int leaf); +void ecall_exception(void); +void ecall_map(void); +size_t ecall_increase_counter(void); +void ecall_producer(void); +void ecall_consumer(void); + +sgx_status_t SGX_CDECL ocall_print_string(const char* str); +sgx_status_t SGX_CDECL ocall_file_exists(const char* filename, int* exists); +sgx_status_t SGX_CDECL ocall_read_file(const char* filename, char* buffer, size_t buffer_size, int* ret); +sgx_status_t SGX_CDECL ocall_write_file(const char* filename, const char* data, size_t data_size, int write_mode, int* ret); +sgx_status_t SGX_CDECL ocall_pointer_user_check(int* val); +sgx_status_t SGX_CDECL ocall_pointer_in(int* val); +sgx_status_t SGX_CDECL ocall_pointer_out(int* val); +sgx_status_t SGX_CDECL ocall_pointer_in_out(int* val); +sgx_status_t SGX_CDECL ocall_function_allow(void); +sgx_status_t SGX_CDECL sgx_oc_cpuidex(int cpuinfo[4], int leaf, int subleaf); +sgx_status_t SGX_CDECL sgx_thread_wait_untrusted_event_ocall(int* retval, const void* self); +sgx_status_t SGX_CDECL sgx_thread_set_untrusted_event_ocall(int* retval, const void* waiter); +sgx_status_t SGX_CDECL sgx_thread_setwait_untrusted_events_ocall(int* retval, const void* waiter, const void* self); +sgx_status_t SGX_CDECL sgx_thread_set_multiple_untrusted_events_ocall(int* retval, const void** waiters, size_t total); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.o new file mode 100644 index 000000000..1986c0e27 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/Enclave_t.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.cpp new file mode 100755 index 000000000..19cae6fe7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include "sgx_cpuid.h" + +#include "sgx_trts.h" +#include "../Enclave.h" +#include "Enclave_t.h" + +/* ecall_malloc_free: + * Uses malloc/free to allocate/free trusted memory. + */ +void ecall_malloc_free(void) +{ + void *ptr = malloc(100); + assert(ptr != NULL); + memset(ptr, 0x0, 100); + free(ptr); +} + +/* ecall_sgx_cpuid: + * Uses sgx_cpuid to get CPU features and types. + */ +void ecall_sgx_cpuid(int cpuinfo[4], int leaf) +{ + sgx_status_t ret = sgx_cpuid(cpuinfo, leaf); + if (ret != SGX_SUCCESS) + abort(); +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.edl new file mode 100755 index 000000000..d21ecaeb9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.edl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Libc.edl - EDL sample for trusted C library. */ + +enclave { + + from "sgx_tstdc.edl" import sgx_oc_cpuidex; + + /* + * A subset of the C99 standard is supported as well as SGX customized functions: + * sgx_cpuid, etc. + */ + + trusted { + /* + * Utilize malloc/free in enclave. + */ + public void ecall_malloc_free(void); + + /* + * Utilize SGX version __cpuid() in enclave. + */ + public void ecall_sgx_cpuid([out] int cpuinfo[4], int leaf); + }; +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.o new file mode 100644 index 000000000..f497bf5ca Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libc.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.cpp new file mode 100755 index 000000000..5320ed6ab --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include + +#include "../Enclave.h" +#include "Enclave_t.h" + +/* + * ecall_exception: + * throw/catch C++ exception inside the enclave. + */ + +void ecall_exception(void) +{ + std::string foo = "foo"; + try { + throw std::runtime_error(foo); + } + catch (std::runtime_error const& e) { + assert( foo == e.what() ); + std::runtime_error clone(""); + clone = e; + assert(foo == clone.what() ); + } + catch (...) { + assert( false ); + } +} + +#include +#include + +using namespace std; + +/* + * ecall_map: + * Utilize STL in the enclave. + */ +void ecall_map(void) +{ + typedef map > map_t; + typedef map_t::value_type map_value; + map_t m; + + m.insert(map_value('a', 1)); + m.insert(map_value('b', 2)); + m.insert(map_value('c', 3)); + m.insert(map_value('d', 4)); + + assert(m['a'] == 1); + assert(m['b'] == 2); + assert(m['c'] == 3); + assert(m['d'] == 4); + + assert(m.find('e') == m.end()); + + return; +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.edl new file mode 100755 index 000000000..206f6cbbc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.edl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* Libcxx.edl - EDL sample for trusted C++ library. */ + +enclave { + + /* + * A subset of the C++03 standard is supported. + */ + + trusted { + /* + * Throw/catch exception inside the enclave. + */ + public void ecall_exception(void); + + /* + * Utilize inside the enclave. + */ + public void ecall_map(void); + }; +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.o new file mode 100644 index 000000000..a636d0a7e Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Libcxx.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.cpp b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.cpp new file mode 100755 index 000000000..86d0ab722 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "../Enclave.h" +#include "Enclave_t.h" + +#include "sgx_thread.h" + +static size_t global_counter = 0; +static sgx_thread_mutex_t global_mutex = SGX_THREAD_MUTEX_INITIALIZER; + +#define BUFFER_SIZE 50 + +typedef struct { + int buf[BUFFER_SIZE]; + int occupied; + int nextin; + int nextout; + sgx_thread_mutex_t mutex; + sgx_thread_cond_t more; + sgx_thread_cond_t less; +} cond_buffer_t; + +static cond_buffer_t buffer = {{0, 0, 0, 0, 0, 0}, 0, 0, 0, + SGX_THREAD_MUTEX_INITIALIZER, SGX_THREAD_COND_INITIALIZER, SGX_THREAD_COND_INITIALIZER}; + +/* + * ecall_increase_counter: + * Utilize thread APIs inside the enclave. + */ +size_t ecall_increase_counter(void) +{ + size_t ret = 0; + for (int i = 0; i < LOOPS_PER_THREAD; i++) { + sgx_thread_mutex_lock(&global_mutex); + /* mutually exclusive adding */ + size_t tmp = global_counter; + global_counter = ++tmp; + if (4*LOOPS_PER_THREAD == global_counter) + ret = global_counter; + sgx_thread_mutex_unlock(&global_mutex); + } + return ret; +} + +void ecall_producer(void) +{ + for (int i = 0; i < 4*LOOPS_PER_THREAD; i++) { + cond_buffer_t *b = &buffer; + sgx_thread_mutex_lock(&b->mutex); + while (b->occupied >= BUFFER_SIZE) + sgx_thread_cond_wait(&b->less, &b->mutex); + b->buf[b->nextin] = b->nextin; + b->nextin++; + b->nextin %= BUFFER_SIZE; + b->occupied++; + sgx_thread_cond_signal(&b->more); + sgx_thread_mutex_unlock(&b->mutex); + } +} + +void ecall_consumer(void) +{ + for (int i = 0; i < LOOPS_PER_THREAD; i++) { + cond_buffer_t *b = &buffer; + sgx_thread_mutex_lock(&b->mutex); + while(b->occupied <= 0) + sgx_thread_cond_wait(&b->more, &b->mutex); + b->buf[b->nextout++] = 0; + b->nextout %= BUFFER_SIZE; + b->occupied--; + sgx_thread_cond_signal(&b->less); + sgx_thread_mutex_unlock(&b->mutex); + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.edl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.edl new file mode 100755 index 000000000..40d2b337c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.edl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Thread.edl - EDL sample for trusted thread library. */ + +enclave { + + from "sgx_tstdc.edl" import sgx_thread_wait_untrusted_event_ocall, sgx_thread_set_untrusted_event_ocall, sgx_thread_setwait_untrusted_events_ocall, sgx_thread_set_multiple_untrusted_events_ocall; + + trusted { + /* + * Use SGX mutex. + */ + public size_t ecall_increase_counter(); + + /* + * Use SGX condition variables. + */ + public void ecall_producer(); + public void ecall_consumer(); + + }; +}; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.o b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.o new file mode 100644 index 000000000..f125afb8b Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/TrustedLibrary/Thread.o differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.01.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.01.xml new file mode 100755 index 000000000..e57be8ae8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.01.xml @@ -0,0 +1,28 @@ + + 0 + 0 + + + + + 10 + 10 + 10 + + 1 + + + 0x40000 + + + + 0x100000 + 0x100000 + 0x100000 + + + 0 + 1 + 0xFFFFFFFE + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.02.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.02.xml new file mode 100755 index 000000000..cc980d5d0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.02.xml @@ -0,0 +1,25 @@ + + 0 + 0 + 0x40000 + + + 0x100000 + 0x40000 + 0x1000 + + 10 + 10 + 10 + 1 + + 0 + 1 + 0xFFFFFFFE + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.03.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.03.xml new file mode 100755 index 000000000..09ca6f231 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.03.xml @@ -0,0 +1,27 @@ + + 0 + 0 + + + 10 + 3 + 1 + + + + 0x40000 + 0x40000 + + + + 0x100000 + + + 0 + 1 + 0xFFFFFFFE + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.04.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.04.xml new file mode 100755 index 000000000..cd5b3fe2d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.04.xml @@ -0,0 +1,23 @@ + + 0 + 0 + + 10 + 3 + 1 + + + + 0x40000 + 0x2000 + + 0x100000 + + + 0 + 1 + 0xFFFFFFFE + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.05.xml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.05.xml new file mode 100755 index 000000000..8ffe35ea2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/config.05.xml @@ -0,0 +1,20 @@ + + 0 + 0 + 1 + 5 + 1 + 13 + 0x4000 + 0x2000 + 0x5000 + + 0x50000 + + 0 + + 1 + 0xFFFFFFFF + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/enclave_keys.txt b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/enclave_keys.txt new file mode 100644 index 000000000..6169f716a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Enclave/enclave_keys.txt @@ -0,0 +1,8 @@ +0400020000000000050d0c0cffff000000000000000000000b000000000000ff0000000000000000c7c1a1bf9aa490a58c0d577ec0b8455571c3d336ecb8118b80a5d4fa9638a583000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000040000000000000000000000000000008e5f81a86bbf4af48c05f3a823098174e84074cb +0400020000000000050d0c0cffff000000000000000000000b000000000000ff00000000000000007a03452159de8c82b7e164b41f0d269bc47f3234839855c88f611e11d683c805000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000800100000000000000000000000000008cd27b61352b06d6adb325621901bbaf0ec29c5e63556e981de1a69f41cc2a5cd873d7ca9b5980bed3779e46e70cb54e89a0f1ba9d23c6c1169e66a41e096bd0f3d6eca5eedaedf798146f235c2c83ba8323705936e2d53a7604ed1b889ebb57444cb9021455c830a44df91ac673d8e4b9d8111858e26cb9a6c45e0254ecbc5ab18de85ee433eedb3a531abb692757cd3b81f50570bcd3fc0e2148b9a99b1555b29a58c94bb56238882ab88941af91423d6382be31f29bb77129ec7f3b49e7451d2fe8df67927bd69a5062704c26c220767d779d3b8fc75734db47d8408656bc15f9acf8a3b5ccdf5e55d23f85e33599b9734d5ea2e57ec8a216d6a1baad5dc6f2b29148f2cbc4e2d38b8684dce8db41c2f618c77e88d9e62b569d2b1447ab06824399efb818c662eab5bbfb9c734440182acd29156cd284eb0b50bc7971d56accaf1bdcb5c239a79d2f0403270f213558a370d636129ab6f0b6cfcbb3d5d940c6051b2e3b791efaa4874b09f368095b65f76a4165aedfcbe6b556d9d78801c842e4226c6963e1f7cf3b73a693d3983b +0400020000000000050d0c0cffff000000000000000000000b000000000000ff000000000000000079daaba6c33796c6d28e3d8b745387a8c741e76b4253da3381edf03a2f780220000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001000000000000000000000000000080010000000000000000000000000000530d24671f32a905d519ddbb01f9a6230cbe43d3560abad5e6f8ecd27897e29dd96f5134aae50be6a9c0497eb667a1948911834bacb98f8a18a8f44f0aa8bc39e75a7f2161f90d47f4b131999b703d058235a5858548b30f2d09bddf966e92d641dd06184cade0d49e9c2f6f598fd5260f408c5a493dc3f48f93e62d0eb2074f8d8429e7f8ccb6f660c80cdca7d5cc60a87bf86fd34d478bc0d726a0b23ba87ae1a8ab682c79bc4eb7bfc8d8a1c171bf1f7ca57dd5a157fc6576e99c7388bbf406ac0a29877b5c40f800f9578edd17b426158b4b579ac0226e6eba9a7e543d92cf6e5864eeac8e0a0006d7cd6f5d0f729004061caa43cfa69ce8bcb56c1bb1ca2e1b2544d3bad10de047722520798514d4aeafdfcc2d18797c05b3a21c221ff44d356b6fd10d406ff7fc6789144e779a9a84ec89556766df713b963a39ebb6e2bee8cc29b669975f13009b512d45c2561c11b49d9d60203b280b8f7c33239539b7f43ef140c7546be8cc8998afa6391e07c3fe905f4336277aa233c4fd55497b02169f963d447ee28f0cc4dabb2c4f67 +0400020000000000050d0c0cffff000000000000000000000b000000000000ff000000000000000039a4239f403092fc743436f15900169f3535a416fdd861a93464d61de41c7469000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001000000000000000000000000000080010000000000000000000000000000f500ad7a6e1e5ef2aebfc6fcdb64f0d84101be6d1d5c37526474b868df0edb62deec11ef9f4c6cf8f6e25a6ac94a12902610da08e804e4d045e21cc096e0b2a41602ef7f829ad6c1ebcce5a0bd7649014120bd0a10f8ae33c0182f8f80cb5df96187580526a6371075352176406def42b1893e45f0b2b4ce55101a17216b65821092c90aff274252bc9e32f223a73dfb3b76ca4350a003d291b9e0ed9a465e9a7055fe0f82c4485a7eaf4df1d38de451850bcc8e5fda54e15f2a2b5dc7c5051aba47abf5ed0d5eca8a2b592fad8ebe1756c0248f469729a4ed5a6690f102f6ff8b5c57f9696e178d9ae84f697a02dccb9c89bc8f74beda28e486cdc18a6d77950e6ad53ee7c7e19f4b30c095d6d188f1d9be312b08b2b5d81998e959dcb47ca9c6a3230635ed80925b301f3c65adaf4f46c803f4a6b90beca59329eb9b374808795b6d14890ea64c6afb7b6731c4eb82b87e1bba74a079f6cd63eedf11df876265610a0e78fe96d5a475165e9bbe3a386d05b2be583dccce63610bbff3c107586168d86f35e2432e0fdf89e19992af36 +0400020000000000050d0c0cffff000000000000000000000b000000000000ff00000000000000006270fb8ecc9b42f9b37f25e16168087448a72f8bc9b9a8d59a3eca45df2610dd000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001000000000000000000000000000080010000000000000000000000000000e21d9b7cdf4028d259adb17cd3a436bd1873b2237414d8f76e8d70e3c2bcfdb06a198cf446330de746a01e38ee0893b60ac828b9cd40bbf104c537cb75b31d8aa868627c783ac2649d73bfe4612dd61df249c76cfab94cf98ff73f810e3b1f2d8f53fe2627b0447bc8b5719a1af343a9db333a77b5a21832510b2beef515286db75670989bb1dfa1c4164dd00e849c87e94041eafe27ed43d074c16972f3b63b6b9fd98716f051e04f6ba0e43541560a41fb9c606d76fccf03af3f010c1e20ff1db5c65e58bbef68e0cffcd26fb072d0f86fd247310ed5b2a42d1f24548facbefadee1599aa5846e558b41e0bc3ab6ad3040f71ee1c8096019b4b38ba41c41caef7480673cc1df966ecfb41c9e6382a744884483df1be17c2e0ed4a043557fb0643dc34fb195c71e55c00d2f944470ecd88c6a9d09e8909ebd8ae89bf5d55c4620fa8c06204c5d9749e25141d720276705e6b2bbb9b0d1d9c8ac51cbb82b51d9d9255ea9eec229246673b7d321aed5d2dd098333eaa950a35225ae15e5504f807bbb7723ec129b40364962348a95ac0d +0400020000000000050d0c0cffff000000000000000000000b000000000000ff0000000000000000496795a549034ac6e0db13a03e05fd4563bfba56f5c88393bcec1a533fcf4087000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001000000000000000000000000000080010000000000000000000000000000101dd6d565c0b19ea6da74e7f0500c8fe46b35f5ba81c5b5952575541620862160d063e34d28e3c1d910f777eb287e338b79460a08ca114f7c91f767d4f013377b10bec8fbd996be4130efad92f8747587bf6bf5511b92388e3a0ab56d1ebd649ead8b3322a621ffff3aaa395899560a7814e9a20fa634d6332a5ec4e0617f366a4940a08a4e2d7aad380fc8399799bdac0f8bc24abe5b3f0d466e464e81c681630bf2b53434c6460302352bf306ba4ace84d5b8fb0c4c31fe81c7593b80b46ee8ef4d5e388039c4948311d7302286e0c5fcd00a7120d5cd5ad9cbfe2961f4a2251daea5b8252904bebba8a8d322e1965dde59d385c9992b90e47d0f9c2cd5907f557ea03e3a2800fe1c5f25e5287add011b332e36c0621aa8dfa9d15d312dd738ed2e2810ed0f9fb4afbbbcb52ca9e509e89e78e21c540fb2377a61ea992279daf0cfc670de9aa86db5a0ba14f105736a36b7bc233663f075922f36c998e34054a520b625782b069527298ca75307e54946a7ff0f3d1ad7cad565287e271913296dc86766c231b4073ba46ede6e4248 +0400020000000000050d0c0cffff000000000000000000000b000000000000ff00000000000000005e3be84c8c761a6ffa02cc623c3baca539f96ee11dfeced5d8a89e0ad4353cd8000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001000000000000000000000000000080010000000000000000000000000000c658ab7146bdf4f0efd695378561fc0ac6cdea7b74fce579b509091b93b7263768e110d49b9c34506e0672688ece953f981cf0ca38e2ec32081ec09e8942ab3e3e28ad94ea5d0293e7a989c18116439f54523e700bfae24618ce4f361577dfaf75a160ecedbd4ede08d37e258cf8ac796c74ef66af7f31d11c738cf7bf50e0ca6af4724cc9b82b829c574295d5145b34f252c43122d712205f0ee334d10903d58aa4d2d1cdd8a455ba6207797511fa04f034496f1b7ec5554a3ef7f673d052509d4741dc1b3bca6df6731b7315082f5a9d5a7cb46aeb2edfa621e46a1885b7fd20776265da3034710d3460020af5e0c1c3eb5725ad8cd792c9b5b372d230b0a7fe26831effa65c28afd7f700c67305258192b166f41e82d3481a7adfd4e2bd2b342c2caf5360d43f17c7bef08490eeb9dd82d9e4708e8b586c77457325159368c7176af972fb7d62f29adeadc2532bb9796dcf7751359700861ac02f7c3ebf09e21429504bfe1ed6cbf7a7ed6be3893d869afb54c5d77c97d42675a6c877cf8a5739bc134bc3bd99f7a8f5e7231eff11 +0400020000000000050d0c0cffff000000000000000000000b000000000000ff0000000000000000eed4b3d1379cfd62c9fbd3db612eccceb00116d4663a3b0724925eb76226a01c000000f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000008001000000000000000000000000000051dc7e06f716bfb36a15caed0e3c8bc02356f6dd55386c2360bd6aec9c5e4f1887748722bb99734f71180b4473b111dbcdc0c5d58757ac7c2d4809dcaa0977883d8a2c0138a8d51f23b92a9d247e7b3cffe27be93680bb2a1f39183f0e4f6b50a3a05b00571013d204894a91bbf3dbf92bd3860102e2f5038a92d16082f473d380270effdabeb9762d1ddee6ed38573988d4f152e8139cffb65201508f30d6dca6333de38a57308e10458f3866b08b4731bab86a8a18de3d85785f28e5e9dde4f15d032754afa7756540c2b2a4714c8355806241bb215ae81d1fd6838152481fd10cad6cd490974550c1f77fc4e00d8da9825a1f03e48bb6da9afde1f5fa2950cabfc607d0ef2b74eb9430e756863e494671e1eb80d4b37d2e35aefd4d0bc0537e93b75ec373affd2f7f5d027ac31348825db0318c3fc1f288c6018228ab0987f687dbb816bd94ac84762ad0e539296d262f1f30f8f7ca150e84969651a609e49d156c7b54ce8483364468d36d932135e7905f9749c50f8f7ec106b1bfdb2574fa63182c634f50597e41a530934019e7 diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Include/user_types.h b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Include/user_types.h new file mode 100755 index 000000000..162987dae --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Include/user_types.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* User defined types */ + + +#define LOOPS_PER_THREAD 500 + +typedef void *buffer_t; +typedef int array_t[10]; + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Makefile b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Makefile new file mode 100755 index 000000000..7a952d85e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/Makefile @@ -0,0 +1,271 @@ +# +# Copyright (C) 2011-2021 Intel Corporation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# + +######## SGX SDK Settings ######## + +SGX_SDK ?= /opt/intel/sgxsdk +SGX_MODE ?= HW +SGX_ARCH ?= x64 +SGX_DEBUG ?= 1 + +include $(SGX_SDK)/buildenv.mk + +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_FLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_FLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_FLAGS += -O0 -g +else + SGX_COMMON_FLAGS += -O2 +endif + +SGX_COMMON_FLAGS += -Wall -Wextra -Winit-self -Wpointer-arith -Wreturn-type \ + -Waddress -Wsequence-point -Wformat-security \ + -Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \ + -Wcast-align -Wcast-qual -Wconversion -Wredundant-decls +SGX_COMMON_CFLAGS := $(SGX_COMMON_FLAGS) -Wjump-misses-init -Wstrict-prototypes -Wunsuffixed-float-constants +SGX_COMMON_CXXFLAGS := $(SGX_COMMON_FLAGS) -Wnon-virtual-dtor -std=c++11 + +######## App Settings ######## + +ifneq ($(SGX_MODE), HW) + Urts_Library_Name := sgx_urts_sim +else + Urts_Library_Name := sgx_urts +endif + +App_Cpp_Files := App/App.cpp $(wildcard App/Edger8rSyntax/*.cpp) $(wildcard App/TrustedLibrary/*.cpp) +App_Include_Paths := -IInclude -IApp -I$(SGX_SDK)/include + +App_C_Flags := -fPIC -Wno-attributes $(App_Include_Paths) + +# Three configuration modes - Debug, prerelease, release +# Debug - Macro DEBUG enabled. +# Prerelease - Macro NDEBUG and EDEBUG enabled. +# Release - Macro NDEBUG enabled. +ifeq ($(SGX_DEBUG), 1) + App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG +else ifeq ($(SGX_PRERELEASE), 1) + App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG +else + App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG +endif + +App_Cpp_Flags := $(App_C_Flags) +App_Link_Flags := -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) -lpthread + +App_Cpp_Objects := $(App_Cpp_Files:.cpp=.o) + +App_Name := app + +######## Enclave Settings ######## + +ifneq ($(SGX_MODE), HW) + Trts_Library_Name := sgx_trts_sim + Service_Library_Name := sgx_tservice_sim +else + Trts_Library_Name := sgx_trts + Service_Library_Name := sgx_tservice +endif +Crypto_Library_Name := sgx_tcrypto + +Enclave_Cpp_Files := Enclave/Enclave.cpp $(wildcard Enclave/Edger8rSyntax/*.cpp) $(wildcard Enclave/TrustedLibrary/*.cpp) +Enclave_Include_Paths := -IInclude -IEnclave -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/libcxx + +Enclave_C_Flags := $(Enclave_Include_Paths) -nostdinc -fvisibility=hidden -fpie -ffunction-sections -fdata-sections $(MITIGATION_CFLAGS) +CC_BELOW_4_9 := $(shell expr "`$(CC) -dumpversion`" \< "4.9") +ifeq ($(CC_BELOW_4_9), 1) + Enclave_C_Flags += -fstack-protector +else + Enclave_C_Flags += -fstack-protector-strong +endif + +Enclave_Cpp_Flags := $(Enclave_C_Flags) -nostdinc++ + +# Enable the security flags +Enclave_Security_Link_Flags := -Wl,-z,relro,-z,now,-z,noexecstack + +# To generate a proper enclave, it is recommended to follow below guideline to link the trusted libraries: +# 1. Link sgx_trts with the `--whole-archive' and `--no-whole-archive' options, +# so that the whole content of trts is included in the enclave. +# 2. For other libraries, you just need to pull the required symbols. +# Use `--start-group' and `--end-group' to link these libraries. +# Do NOT move the libraries linked with `--start-group' and `--end-group' within `--whole-archive' and `--no-whole-archive' options. +# Otherwise, you may get some undesirable errors. +Enclave_Link_Flags := $(MITIGATION_LDFLAGS) $(Enclave_Security_Link_Flags) \ + -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_TRUSTED_LIBRARY_PATH) \ + -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ + -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \ + -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ + -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ + -Wl,--defsym,__ImageBase=0 -Wl,--gc-sections \ + -Wl,--version-script=Enclave/Enclave.lds + +Enclave_Cpp_Objects := $(sort $(Enclave_Cpp_Files:.cpp=.o)) + +Enclave_Name := enclave.so +Signed_Enclave_Name := enclave.signed.so +Enclave_Config_File := Enclave/Enclave.config.xml +Enclave_Test_Key := Enclave/Enclave_private_test.pem + +ifeq ($(SGX_MODE), HW) +ifeq ($(SGX_DEBUG), 1) + Build_Mode = HW_DEBUG +else ifeq ($(SGX_PRERELEASE), 1) + Build_Mode = HW_PRERELEASE +else + Build_Mode = HW_RELEASE +endif +else +ifeq ($(SGX_DEBUG), 1) + Build_Mode = SIM_DEBUG +else ifeq ($(SGX_PRERELEASE), 1) + Build_Mode = SIM_PRERELEASE +else + Build_Mode = SIM_RELEASE +endif +endif + + +.PHONY: all target run +all: .config_$(Build_Mode)_$(SGX_ARCH) + @$(MAKE) target + +ifeq ($(Build_Mode), HW_RELEASE) +target: $(App_Name) $(Enclave_Name) + @echo "The project has been built in release hardware mode." + @echo "Please sign the $(Enclave_Name) first with your signing key before you run the $(App_Name) to launch and access the enclave." + @echo "To sign the enclave use the command:" + @echo " $(SGX_ENCLAVE_SIGNER) sign -key -enclave $(Enclave_Name) -out <$(Signed_Enclave_Name)> -config $(Enclave_Config_File)" + @echo "You can also sign the enclave using an external signing tool." + @echo "To build the project in simulation mode set SGX_MODE=SIM. To build the project in prerelease mode set SGX_PRERELEASE=1 and SGX_MODE=HW." + + +else +target: $(App_Name) $(Signed_Enclave_Name) +ifeq ($(Build_Mode), HW_DEBUG) + @echo "The project has been built in debug hardware mode." +else ifeq ($(Build_Mode), SIM_DEBUG) + @echo "The project has been built in debug simulation mode." +else ifeq ($(Build_Mode), HW_PRERELEASE) + @echo "The project has been built in pre-release hardware mode." +else ifeq ($(Build_Mode), SIM_PRERELEASE) + @echo "The project has been built in pre-release simulation mode." +else + @echo "The project has been built in release simulation mode." +endif + +endif + +run: all +ifneq ($(Build_Mode), HW_RELEASE) + @$(CURDIR)/$(App_Name) + @echo "RUN => $(App_Name) [$(SGX_MODE)|$(SGX_ARCH), OK]" +endif + +.config_$(Build_Mode)_$(SGX_ARCH): + @rm -f .config_* $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.* + @touch .config_$(Build_Mode)_$(SGX_ARCH) + +######## App Objects ######## + +App/Enclave_u.h: $(SGX_EDGER8R) Enclave/Enclave.edl + @cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl --search-path ../Enclave --search-path $(SGX_SDK)/include + @echo "GEN => $@" + +App/Enclave_u.c: App/Enclave_u.h + +App/Enclave_u.o: App/Enclave_u.c + @$(CC) $(SGX_COMMON_CFLAGS) $(App_C_Flags) -c $< -o $@ + @echo "CC <= $<" + +App/%.o: App/%.cpp App/Enclave_u.h + @$(CXX) $(SGX_COMMON_CXXFLAGS) $(App_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +$(App_Name): App/Enclave_u.o $(App_Cpp_Objects) + @$(CXX) $^ -o $@ $(App_Link_Flags) + @echo "LINK => $@" + +######## Enclave Objects ######## + +Enclave/Enclave_t.h: $(SGX_EDGER8R) Enclave/Enclave.edl + @cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl --search-path ../Enclave --search-path $(SGX_SDK)/include + @echo "GEN => $@" + +Enclave/Enclave_t.c: Enclave/Enclave_t.h + +Enclave/Enclave_t.o: Enclave/Enclave_t.c + @$(CC) $(SGX_COMMON_CFLAGS) $(Enclave_C_Flags) -c $< -o $@ + @echo "CC <= $<" + +Enclave/%.o: Enclave/%.cpp Enclave/Enclave_t.h + @$(CXX) $(SGX_COMMON_CXXFLAGS) $(Enclave_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +$(Enclave_Name): Enclave/Enclave_t.o $(Enclave_Cpp_Objects) + @$(CXX) $^ -o $@ $(Enclave_Link_Flags) + @echo "LINK => $@" + +$(Signed_Enclave_Name): $(Enclave_Name) +ifeq ($(wildcard $(Enclave_Test_Key)),) + @echo "There is no enclave test key ." + @echo "The project will generate a key for test." + @openssl genrsa -out $(Enclave_Test_Key) -3 3072 +endif + @$(SGX_ENCLAVE_SIGNER) sign -key $(Enclave_Test_Key) -enclave $(Enclave_Name) -out $@ -config $(Enclave_Config_File) + @echo "SIGN => $@" + +.PHONY: clean + +clean: + @rm -f .config_* $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.* $(Enclave_Test_Key) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/README.txt b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/README.txt new file mode 100755 index 000000000..07c1b6df7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/README.txt @@ -0,0 +1,103 @@ +------------------------ +Purpose of SampleEnclave +------------------------ +The project demonstrates several fundamental usages of Intel(R) Software Guard +Extensions (Intel(R) SGX) SDK: +- Initializing and destroying an enclave +- Creating ECALLs or OCALLs +- Calling trusted libraries inside the enclave + +------------------------------------ +How to Build/Execute the Sample Code +------------------------------------ +1. Install Intel(R) SGX SDK for Linux* OS +2. Enclave test key(two options): + a. Install openssl first, then the project will generate a test key automatically when you build the project. + b. Rename your test key(3072-bit RSA private key) to and put it under the folder. +3. Make sure your environment is set: + $ source ${sgx-sdk-install-path}/environment +4. Build the project with the prepared Makefile: + a. Hardware Mode, Debug build: + 1) Enclave with no mitigation: + $ make + 2) Enclave with mitigations for indirects and returns only: + $ make MITIGATION-CVE-2020-0551=CF + 3) Enclave with full mitigation: + $ make MITIGATION-CVE-2020-0551=LOAD + b. Hardware Mode, Pre-release build: + 1) Enclave with no mitigation: + $ make SGX_PRERELEASE=1 SGX_DEBUG=0 + 2) Enclave with mitigations for indirects and returns only: + $ make SGX_PRERELEASE=1 SGX_DEBUG=0 MITIGATION-CVE-2020-0551=CF + 3) Enclave with full mitigation: + $ make SGX_PRERELEASE=1 SGX_DEBUG=0 MITIGATION-CVE-2020-0551=LOAD + c. Hardware Mode, Release build: + 1) Enclave with no mitigation: + $ make SGX_DEBUG=0 + 2) Enclave with mitigations for indirects and returns only: + $ make SGX_DEBUG=0 MITIGATION-CVE-2020-0551=CF + 3) Enclave with full mitigation: + $ make SGX_DEBUG=0 MITIGATION-CVE-2020-0551=LOAD + d. Simulation Mode, Debug build: + $ make SGX_MODE=SIM + e. Simulation Mode, Pre-release build: + $ make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0 + f. Simulation Mode, Release build: + $ make SGX_MODE=SIM SGX_DEBUG=0 +5. Execute the binary directly: + $ ./app +6. Remember to "make clean" before switching build mode + +------------------------------------------ +Explanation about Configuration Parameters +------------------------------------------ +TCSMaxNum, TCSNum, TCSMinPool + + These three parameters will determine whether a thread will be created + dynamically when there is no available thread to do the work. + + +StackMaxSize, StackMinSize + + For a dynamically created thread, StackMinSize is the amount of stack available + once the thread is created and StackMaxSize is the total amount of stack that + thread can use. The gap between StackMinSize and StackMaxSize is the stack + dynamically expanded as necessary at runtime. + + For a static thread, only StackMaxSize is relevant which specifies the total + amount of stack available to the thread. + + +HeapMaxSize, HeapInitSize, HeapMinSize + + HeapMinSize is the amount of heap available once the enclave is initialized. + + HeapMaxSize is the total amount of heap an enclave can use. The gap between + HeapMinSize and HeapMaxSize is the heap dynamically expanded as necessary + at runtime. + + HeapInitSize is here for compatibility. + +------------------------------------------------- +Sample configuration files for the Sample Enclave +------------------------------------------------- +With below configurations, if the signed enclave is launched on a SGX2 platform +with SGX2 supported kernel, it will be loaded with EDMM enabled. Otherwise, it +will behave in way of SGX1. + + config.01.xml: There is no dynamic thread, no dynamic heap expansion. + config.02.xml: There is no dynamic thread. But dynamic heap expansion can happen. + config.03.xml: There are dynamic threads. For a dynamic thread, there's no stack expansion. + config.04.xml: There are dynamic threads. For a dynamic thread, stack will expanded as necessary. + +Below configuration is only workable on a SGX2 platform with SGX2 supported kernel: + + config.05.xml: There is a user region where users could operate on. + +------------------------------------------------- +Launch token initialization +------------------------------------------------- +If using libsgx-enclave-common or sgxpsw under version 2.4, an initialized variable launch_token needs to be passed as the 3rd parameter of API sgx_create_enclave. For example, + +sgx_launch_token_t launch_token = {0}; +sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL); diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/app b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/app new file mode 100755 index 000000000..9407359a6 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/app differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/common/shared_macros.h b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/common/shared_macros.h new file mode 100755 index 000000000..5f7810e7b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/common/shared_macros.h @@ -0,0 +1,33 @@ +#ifndef SHARED_MACROS_H +#define SHARED_MACROS_H + + +// 内存分配宏 +#define RSA_KEY_SIZE 384 // RSA密钥的长度(固定384字节) +#define MAX_FILE_SIZE 100000 // 单次读取文件的最大容量 + +#define MAX_TRANSACTION_SIZE 500 // 一条普通交易信息的最大长度(原字符串) +#define MAX_TRANSACTION_ENCRYPTED_SIZE (RSA_KEY_SIZE * 6 + 100) // 一条加密后的交易信息的最大长度(十六进制密文的字符串) +#define MAX_PLAINTEXT_SIZE RSA_KEY_SIZE // 最长的单块明文长度(二进制) +#define MAX_CIPHERTEXT_SIZE RSA_KEY_SIZE // 最长的密文长度(二进制) + +#define MAX_SEAL_DATA_SIZE 1200 // 密封后数据的最大长度 +#define MAX_UNSEAL_DATA_SIZE 600 // 解封后数据的最大长度(可以用来密封的最长字节) + +// 读写模式相关宏 +#define OVER_WRITE_MODE 1 // 覆写模式 +#define APPEND_MODE 0 // 追加模式 + +// 交易混洗相关宏 +#define POOL_SIZE 10 // 钱包池个数 + +// 交易预警宏 +#define TRANSACTION_NO_EXCEPTION 0 // 无异常 +#define EXCESSIVE_SINGLE_AMOUNT 1 // 单笔交易金额过大 +#define EXCESSIVE_DAILY_AMOUNT 2 // 日交易金额过大 +#define EXCESSIVE_FREQUENCY 3 // 交易过于频繁 + +#define MAX_SINGLE_AMOUNT 100000 // 单笔交易最大金额 +#define MAX_DAILY_AMOUNT 500000 // 日交易最大金额 + +#endif // SHARED_MACROS_H diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/enclave.signed.so b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/enclave.signed.so new file mode 100644 index 000000000..1c528142d Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/enclave.signed.so differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/enclave.so b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/enclave.so new file mode 100755 index 000000000..78d718cdd Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/enclave.so differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/nohup.out b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/nohup.out new file mode 100644 index 000000000..b07bf981a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/nohup.out @@ -0,0 +1,529 @@ + + Checksum(0x0x7ffcba70a890, 100) = 0xfffd4143 +Info: executing thread synchronization, please wait... +Server is running on http://0.0.0.0:8082 +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +Error: fail to decrypt "from". +Fail to decrypt trasaction data. +Failed to decrypt transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +Error: fail to decrypt "from". +Fail to decrypt trasaction data. +Failed to decrypt transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount11.000000 +Successfully decrypt transaction. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +No exception detected +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 6 Amount: 0.63 +Address: 4 Amount: 0.53 +Address: 3 Amount: 9.84 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount1199999.000000 +Successfully decrypt transaction. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +Exception detected: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount0.010000 +Successfully decrypt transaction. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +No exception detected +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 9 Amount: 0.00 +Address: 2 Amount: 0.00 +Address: 4 Amount: 0.00 +Address: 1 Amount: 0.00 +Address: 3 Amount: 0.01 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount11.000000 +Successfully decrypt transaction. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +No exception detected +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 4 Amount: 0.38 +Address: 5 Amount: 2.96 +Address: 2 Amount: 2.79 +Address: 8 Amount: 4.86 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount1.000000 +Successfully decrypt transaction. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +No exception detected +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 9 Amount: 0.26 +Address: 7 Amount: 0.34 +Address: 4 Amount: 0.12 +Address: 6 Amount: 0.10 +Address: 1 Amount: 0.17 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet12.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 12 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 6 Amount: 0.19 +Address: 9 Amount: 0.02 +Address: 7 Amount: 0.79 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet11.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 11 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 1 Amount: 0.45 +Address: 9 Amount: 0.21 +Address: 7 Amount: 0.13 +Address: 6 Amount: 0.21 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet11.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 11 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 8 Amount: 0.38 +Address: 9 Amount: 0.13 +Address: 7 Amount: 0.11 +Address: 2 Amount: 0.10 +Address: 3 Amount: 0.28 +Transaction shuffled successfully. +Error: fail to open file . +Error: failed to read data from outer. +Failed to read wallet file: Wallets/wallet11.txt. +Error: failed to read RSA key params from file. +Error: Failed to get RSA keys for wallet_id 11 +Failed to encrypt transaction. +Failed to shuffle transaction. +-------------------- +Checksum(0x0x7ffec7b13760, 100) = 0xfffd4143 +Info: executing thread synchronization, please wait... +Server is running on http://0.0.0.0:8082 +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 8 Amount: 164425.71 +Address: 2 Amount: 91520.69 +Address: 3 Amount: 264170.74 +Address: 5 Amount: 98402.93 +Address: 9 Amount: 68430.61 +Address: 6 Amount: 122428.85 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 4 Amount: 202797.52 +Address: 3 Amount: 218772.71 +Address: 5 Amount: 89401.75 +Address: 8 Amount: 58333.56 +Address: 6 Amount: 127516.56 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 8 Amount: 98398.11 +Address: 2 Amount: 60562.32 +Address: 4 Amount: 193061.20 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 6 Amount: 127960.60 +Address: 1 Amount: 171233.94 +Address: 8 Amount: 169769.89 +Address: 3 Amount: 78161.22 +Address: 5 Amount: 519.47 +Address: 7 Amount: 96457.78 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 3 Amount: 361825.54 +Address: 5 Amount: 6969.84 +Address: 1 Amount: 52353.42 +Address: 6 Amount: 406330.99 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 2 Amount: 2844.26 +Address: 4 Amount: 20060.20 +Address: 1 Amount: 6416.30 +Address: 7 Amount: 11191.78 +Address: 9 Amount: 9084.30 +Address: 6 Amount: 16084.34 +Transaction shuffled successfully. +Warning sign: 0 +Message: No exception detected in the transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 2 Amount: 41935.38 +Address: 6 Amount: 38883.11 +Address: 5 Amount: 66276.60 +Address: 9 Amount: 108092.13 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 6 Amount: 290677.56 +Address: 2 Amount: 244719.32 +Address: 3 Amount: 39982.22 +Address: 5 Amount: 281567.56 +Transaction shuffled successfully. +Warning sign: 1 +Message: Amount exceeds the maximum allowed for a single transaction. +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 2 Amount: 28515.37 +Address: 1 Amount: 15986.82 +Address: 4 Amount: 5114.76 +Address: 7 Amount: 6041.09 +Address: 5 Amount: 7213.91 +Transaction shuffled successfully. +Warning sign: 0 +Message: No exception detected in the transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 50 +to: 68 +amount929603.710000 +Successfully decrypt transaction. +RSA key params loaded successfully from file Wallets/wallet50.txt. +Private key restored! +Public key restored! +Data to encrypt: +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 1 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet1.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 2 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet2.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 3 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet3.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 4 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet4.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 5 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet5.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 6 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet6.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 7 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet7.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 8 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet8.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 9 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet9.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 10 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet10.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 11 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet11.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a post request for creating a new wallet. +Generating key pair for wallet_id 12 +Key params created! +Private key created! +Public key created! +Successfully write rsa key params into file. +RSA key params loaded successfully from file Wallets/wallet12.txt. +Private key restored! +Public key restored! +Restored key pair from file passed 4/4 test. +Successfully created a new wallet. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount10.000000 +Successfully decrypt transaction. +RSA key params loaded successfully from file Wallets/wallet12.txt. +Private key restored! +Public key restored! +Data to encrypt: +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +No exception detected +-------------------- +Received a POST request to shuffle transaction. +Shuffling transaction: +Address: 8 Amount: 0.18 +Address: 3 Amount: 1.24 +Address: 5 Amount: 3.89 +Address: 1 Amount: 0.15 +Address: 6 Amount: 4.53 +Transaction shuffled successfully. +Warning sign: 0 +Message: No exception detected in the transaction. +-------------------- +Received a POST request to decrypt transaction. +RSA key params loaded successfully from file Enclave/enclave_keys.txt. +Private key restored! +Public key restored! +from: 12 +to: 11 +amount1199983.000000 +Successfully decrypt transaction. +RSA key params loaded successfully from file Wallets/wallet12.txt. +Private key restored! +Public key restored! +Data to encrypt: +Successfully write encrypted transaction into file. +Transaction decrypted successfully. +-------------------- +Received a POST request of transaction warning. +Exception detected: Amount exceeds the maximum allowed for a single transaction. +-------------------- diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/run.sh b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/run.sh new file mode 100755 index 000000000..f54c88414 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/TEE/run.sh @@ -0,0 +1,6 @@ +#! /bin/bash + +make clean +make +./app + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/config/config.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/config/config.go new file mode 100644 index 000000000..a8f4d51f0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/config/config.go @@ -0,0 +1,141 @@ +package config + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io" + "log" + "os" + "path/filepath" + + "github.com/FISCO-BCOS/go-sdk/v3/client" + "gopkg.in/yaml.v2" +) + +type Config struct { + Database struct { + Username string `yaml:"username"` + Password string `yaml:"password"` + Host string `yaml:"host"` + Port int `yaml:"port"` + DBName string `yaml:"dbname"` + Charset string `yaml:"charset"` + } `yaml:"database"` + + Fisco struct { + IsSMCrypto bool `yaml:"is_sm_crypto"` + GroupID string `yaml:"group_id"` + DisableSsl bool `yaml:"disable_ssl"` + Host string `yaml:"host"` + Port int `yaml:"port"` + TLSCaFile string `yaml:"tls_ca_file"` + TLSKeyFile string `yaml:"tls_key_file"` + TLSCertFile string `yaml:"tls_cert_file"` + Authorizationtoken string `yaml:"Authorizationtoken"` + } `yaml:"fisco"` + + Contract struct { + KeyShareContract string `yaml:"key_share_contract"` + DecisionContract string `yaml:"decision_contract"` + } `yaml:"contract"` + + Tee struct { + PrivateKey string `yaml:"private_key"` + PublicKey string `yaml:"public_key"` + } `yaml:"tee"` +} + +var GlobalConfig Config + +func LoadConfig(configPath string) (*Config, error) { + config := &Config{} + + file, err := os.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("error reading config file: %v", err) + } + + err = yaml.Unmarshal(file, config) + if err != nil { + return nil, fmt.Errorf("error parsing config file: %v", err) + } + + GlobalConfig = *config + return config, nil +} + +func ReadConfig(pk string) *client.Config { + privateKey, _ := hex.DecodeString(pk) + + config := &client.Config{ + IsSMCrypto: GlobalConfig.Fisco.IsSMCrypto, + GroupID: GlobalConfig.Fisco.GroupID, + DisableSsl: GlobalConfig.Fisco.DisableSsl, + PrivateKey: privateKey, + Host: GlobalConfig.Fisco.Host, + Port: GlobalConfig.Fisco.Port, + TLSCaFile: GlobalConfig.Fisco.TLSCaFile, + TLSKeyFile: GlobalConfig.Fisco.TLSKeyFile, + TLSCertFile: GlobalConfig.Fisco.TLSCertFile, + } + + return config +} + +type KeyData struct { + PublicKey string `json:"publicKey"` + PrivateKey string `json:"privateKey"` +} + +func ReadJuryKeys() ([]string, []string) { + juryPrivateKeys := []string{} + juryPublicKeys := []string{} + + // Define the directory containing the key files + dir := "config/keys" + + // Read all files in the directory + files, err := os.ReadDir(dir) + if err != nil { + log.Fatalf("Failed to read directory: %v", err) + } + + // Iterate over each file + for _, file := range files { + // Construct the full file path + filePath := filepath.Join(dir, file.Name()) + + // Open the file + jsonFile, err := os.Open(filePath) + if err != nil { + log.Printf("Failed to open file %s: %v", filePath, err) + continue + } + + // Read the file contents + byteValue, err := io.ReadAll(jsonFile) + if err != nil { + log.Printf("Failed to read file %s: %v", filePath, err) + jsonFile.Close() + continue + } + + // Close the file + jsonFile.Close() + + // Parse the JSON + var keyData KeyData + err = json.Unmarshal(byteValue, &keyData) + if err != nil { + log.Printf("Failed to parse JSON from file %s: %v", filePath, err) + continue + } + + // Append the keys to the respective slices + juryPrivateKeys = append(juryPrivateKeys, keyData.PrivateKey) + juryPublicKeys = append(juryPublicKeys, keyData.PublicKey) + } + + return juryPrivateKeys, juryPublicKeys +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/config/config.yaml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/config/config.yaml new file mode 100644 index 000000000..af28c51e8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/config/config.yaml @@ -0,0 +1,26 @@ +database: + username: root + password: "zss123456zss" + host: 127.0.0.1 + port: 3306 + dbname: gva + charset: utf8mb4 + +fisco: + is_sm_crypto: false + group_id: "group0" + disable_ssl: false + host: "127.0.0.1" + port: 20200 + tls_ca_file: "./config/sdk/ca.crt" + tls_key_file: "./config/sdk/sdk.key" + tls_cert_file: "./config/sdk/sdk.crt" + Authorizationtoken: "Token 213cd1766722b0b51238b25f4868bad4d0c1b616e2ad71cde9fa1c89a3fa88ac" + +contract: + key_share_contract: "0x6849f21d1e455e9f0712b1e99fa4fcd23758e8f1" + decision_contract: "0x4721d1a77e0e76851d460073e64ea06d9c104194" + +tee: + private_key: "a1d6e8b17cabcaea1374facffca5474338af69d86de0f4137b7ba4c744338148" + public_key: "22acd867175ab1cf5b3da4b0db073e7c687bb5c7fbba6b7f2e4a3087d2aa9d92a9711922eacf680fe35dc60c5ce7a698115568b4a6cf1e6c397f20a886802817" \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.abi new file mode 100644 index 000000000..f9d8f7663 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"evidence","type":"string"},{"indexed":false,"internalType":"string","name":"nodeID","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"DecisionRecorded","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"decisions","outputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"approved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getDecision","outputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"approved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDecisionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"recordDecision","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"evidence","type":"string"}],"name":"verifyEvidence","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.bin new file mode 100644 index 000000000..64acb8752 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610b7e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636f13f95a1461005c578063af7548481461008c578063b829df82146100bf578063b8c0dbbd146100f2578063e4ff19da14610122575b600080fd5b610076600480360381019061007191906107ae565b610140565b6040516100839190610812565b60405180910390f35b6100a660048036038101906100a19190610863565b61014b565b6040516100b69493929190610927565b60405180910390f35b6100d960048036038101906100d49190610863565b610335565b6040516100e99493929190610927565b60405180910390f35b61010c600480360381019061010791906109a6565b610492565b6040516101199190610812565b60405180910390f35b61012a6105a5565b6040516101379190610a31565b60405180910390f35b600060019050919050565b6060806000806000805490508510610198576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018f90610a98565b60405180910390fd5b60008086815481106101ad576101ac610ab8565b5b90600052602060002090600402016040518060800160405290816000820180546101d690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461020290610b16565b801561024f5780601f106102245761010080835404028352916020019161024f565b820191906000526020600020905b81548152906001019060200180831161023257829003601f168201915b5050505050815260200160018201805461026890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461029490610b16565b80156102e15780601f106102b6576101008083540402835291602001916102e1565b820191906000526020600020905b8154815290600101906020018083116102c457829003601f168201915b50505050508152602001600282015481526020016003820160009054906101000a900460ff161515151581525050905080600001518160200151826040015183606001519450945094509450509193509193565b6000818154811061034557600080fd5b906000526020600020906004020160009150905080600001805461036890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461039490610b16565b80156103e15780601f106103b6576101008083540402835291602001916103e1565b820191906000526020600020905b8154815290600101906020018083116103c457829003601f168201915b5050505050908060010180546103f690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461042290610b16565b801561046f5780601f106104445761010080835404028352916020019161046f565b820191906000526020600020905b81548152906001019060200180831161045257829003601f168201915b5050505050908060020154908060030160009054906101000a900460ff16905084565b60008060405180608001604052808681526020018581526020014281526020018415158152509050600081908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000190805190602001906105029291906105b1565b50602082015181600101908051906020019061051f9291906105b1565b506040820151816002015560608201518160030160006101000a81548160ff02191690831515021790555050507f641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb08542881600001518260200151836040015184606001516040516105919493929190610927565b60405180910390a160019150509392505050565b60008080549050905090565b8280546105bd90610b16565b90600052602060002090601f0160209004810192826105df5760008555610626565b82601f106105f857805160ff1916838001178555610626565b82800160010185558215610626579182015b8281111561062557825182559160200191906001019061060a565b5b5090506106339190610637565b5090565b5b80821115610650576000816000905550600101610638565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6106bb82610672565b810181811067ffffffffffffffff821117156106da576106d9610683565b5b80604052505050565b60006106ed610654565b90506106f982826106b2565b919050565b600067ffffffffffffffff82111561071957610718610683565b5b61072282610672565b9050602081019050919050565b82818337600083830152505050565b600061075161074c846106fe565b6106e3565b90508281526020810184848401111561076d5761076c61066d565b5b61077884828561072f565b509392505050565b600082601f83011261079557610794610668565b5b81356107a584826020860161073e565b91505092915050565b6000602082840312156107c4576107c361065e565b5b600082013567ffffffffffffffff8111156107e2576107e1610663565b5b6107ee84828501610780565b91505092915050565b60008115159050919050565b61080c816107f7565b82525050565b60006020820190506108276000830184610803565b92915050565b6000819050919050565b6108408161082d565b811461084b57600080fd5b50565b60008135905061085d81610837565b92915050565b6000602082840312156108795761087861065e565b5b60006108878482850161084e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156108ca5780820151818401526020810190506108af565b838111156108d9576000848401525b50505050565b60006108ea82610890565b6108f4818561089b565b93506109048185602086016108ac565b61090d81610672565b840191505092915050565b6109218161082d565b82525050565b6000608082019050818103600083015261094181876108df565b9050818103602083015261095581866108df565b90506109646040830185610918565b6109716060830184610803565b95945050505050565b610983816107f7565b811461098e57600080fd5b50565b6000813590506109a08161097a565b92915050565b6000806000606084860312156109bf576109be61065e565b5b600084013567ffffffffffffffff8111156109dd576109dc610663565b5b6109e986828701610780565b935050602084013567ffffffffffffffff811115610a0a57610a09610663565b5b610a1686828701610780565b9250506040610a2786828701610991565b9150509250925092565b6000602082019050610a466000830184610918565b92915050565b7f496e646578206f7574206f6620626f756e647300000000000000000000000000600082015250565b6000610a8260138361089b565b9150610a8d82610a4c565b602082019050919050565b60006020820190508181036000830152610ab181610a75565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610b2e57607f821691505b60208210811415610b4257610b41610ae7565b5b5091905056fea2646970667358221220977b96c748e0423ec306543f50d16911b1ba1f1f2928bff91954f64010dea1d264736f6c634300080b0033 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.go new file mode 100644 index 000000000..e28063252 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.go @@ -0,0 +1,445 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package Decision + +import ( + "fmt" + "math/big" + "strings" + + "github.com/FISCO-BCOS/go-sdk/v3/abi" + "github.com/FISCO-BCOS/go-sdk/v3/abi/bind" + "github.com/FISCO-BCOS/go-sdk/v3/types" + "github.com/ethereum/go-ethereum/common" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = abi.U256 + _ = bind.Bind + _ = common.Big1 +) + +// DecisionStorageABI is the input ABI used to generate the binding from. +const DecisionStorageABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"DecisionRecorded\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"decisions\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getDecision\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDecisionCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"nodeID\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"recordDecision\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"evidence\",\"type\":\"string\"}],\"name\":\"verifyEvidence\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" + +// DecisionStorageBin is the compiled bytecode used for deploying new contracts. +var DecisionStorageBin = "0x608060405234801561001057600080fd5b50610b7e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636f13f95a1461005c578063af7548481461008c578063b829df82146100bf578063b8c0dbbd146100f2578063e4ff19da14610122575b600080fd5b610076600480360381019061007191906107ae565b610140565b6040516100839190610812565b60405180910390f35b6100a660048036038101906100a19190610863565b61014b565b6040516100b69493929190610927565b60405180910390f35b6100d960048036038101906100d49190610863565b610335565b6040516100e99493929190610927565b60405180910390f35b61010c600480360381019061010791906109a6565b610492565b6040516101199190610812565b60405180910390f35b61012a6105a5565b6040516101379190610a31565b60405180910390f35b600060019050919050565b6060806000806000805490508510610198576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018f90610a98565b60405180910390fd5b60008086815481106101ad576101ac610ab8565b5b90600052602060002090600402016040518060800160405290816000820180546101d690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461020290610b16565b801561024f5780601f106102245761010080835404028352916020019161024f565b820191906000526020600020905b81548152906001019060200180831161023257829003601f168201915b5050505050815260200160018201805461026890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461029490610b16565b80156102e15780601f106102b6576101008083540402835291602001916102e1565b820191906000526020600020905b8154815290600101906020018083116102c457829003601f168201915b50505050508152602001600282015481526020016003820160009054906101000a900460ff161515151581525050905080600001518160200151826040015183606001519450945094509450509193509193565b6000818154811061034557600080fd5b906000526020600020906004020160009150905080600001805461036890610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461039490610b16565b80156103e15780601f106103b6576101008083540402835291602001916103e1565b820191906000526020600020905b8154815290600101906020018083116103c457829003601f168201915b5050505050908060010180546103f690610b16565b80601f016020809104026020016040519081016040528092919081815260200182805461042290610b16565b801561046f5780601f106104445761010080835404028352916020019161046f565b820191906000526020600020905b81548152906001019060200180831161045257829003601f168201915b5050505050908060020154908060030160009054906101000a900460ff16905084565b60008060405180608001604052808681526020018581526020014281526020018415158152509050600081908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000190805190602001906105029291906105b1565b50602082015181600101908051906020019061051f9291906105b1565b506040820151816002015560608201518160030160006101000a81548160ff02191690831515021790555050507f641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb08542881600001518260200151836040015184606001516040516105919493929190610927565b60405180910390a160019150509392505050565b60008080549050905090565b8280546105bd90610b16565b90600052602060002090601f0160209004810192826105df5760008555610626565b82601f106105f857805160ff1916838001178555610626565b82800160010185558215610626579182015b8281111561062557825182559160200191906001019061060a565b5b5090506106339190610637565b5090565b5b80821115610650576000816000905550600101610638565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6106bb82610672565b810181811067ffffffffffffffff821117156106da576106d9610683565b5b80604052505050565b60006106ed610654565b90506106f982826106b2565b919050565b600067ffffffffffffffff82111561071957610718610683565b5b61072282610672565b9050602081019050919050565b82818337600083830152505050565b600061075161074c846106fe565b6106e3565b90508281526020810184848401111561076d5761076c61066d565b5b61077884828561072f565b509392505050565b600082601f83011261079557610794610668565b5b81356107a584826020860161073e565b91505092915050565b6000602082840312156107c4576107c361065e565b5b600082013567ffffffffffffffff8111156107e2576107e1610663565b5b6107ee84828501610780565b91505092915050565b60008115159050919050565b61080c816107f7565b82525050565b60006020820190506108276000830184610803565b92915050565b6000819050919050565b6108408161082d565b811461084b57600080fd5b50565b60008135905061085d81610837565b92915050565b6000602082840312156108795761087861065e565b5b60006108878482850161084e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156108ca5780820151818401526020810190506108af565b838111156108d9576000848401525b50505050565b60006108ea82610890565b6108f4818561089b565b93506109048185602086016108ac565b61090d81610672565b840191505092915050565b6109218161082d565b82525050565b6000608082019050818103600083015261094181876108df565b9050818103602083015261095581866108df565b90506109646040830185610918565b6109716060830184610803565b95945050505050565b610983816107f7565b811461098e57600080fd5b50565b6000813590506109a08161097a565b92915050565b6000806000606084860312156109bf576109be61065e565b5b600084013567ffffffffffffffff8111156109dd576109dc610663565b5b6109e986828701610780565b935050602084013567ffffffffffffffff811115610a0a57610a09610663565b5b610a1686828701610780565b9250506040610a2786828701610991565b9150509250925092565b6000602082019050610a466000830184610918565b92915050565b7f496e646578206f7574206f6620626f756e647300000000000000000000000000600082015250565b6000610a8260138361089b565b9150610a8d82610a4c565b602082019050919050565b60006020820190508181036000830152610ab181610a75565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610b2e57607f821691505b60208210811415610b4257610b41610ae7565b5b5091905056fea2646970667358221220977b96c748e0423ec306543f50d16911b1ba1f1f2928bff91954f64010dea1d264736f6c634300080b0033" +var DecisionStorageSMBin = "0x" + +// DeployDecisionStorage deploys a new contract, binding an instance of DecisionStorage to it. +func DeployDecisionStorage(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Receipt, *DecisionStorage, error) { + parsed, err := abi.JSON(strings.NewReader(DecisionStorageABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(DecisionStorageSMBin) + } else { + bytecode = common.FromHex(DecisionStorageBin) + } + if len(bytecode) == 0 { + return common.Address{}, nil, nil, fmt.Errorf("cannot deploy empty bytecode") + } + address, receipt, contract, err := bind.DeployContract(auth, parsed, bytecode, DecisionStorageABI, backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, receipt, &DecisionStorage{DecisionStorageCaller: DecisionStorageCaller{contract: contract}, DecisionStorageTransactor: DecisionStorageTransactor{contract: contract}, DecisionStorageFilterer: DecisionStorageFilterer{contract: contract}}, nil +} + +func AsyncDeployDecisionStorage(auth *bind.TransactOpts, handler func(*types.Receipt, error), backend bind.ContractBackend) (*types.Transaction, error) { + parsed, err := abi.JSON(strings.NewReader(DecisionStorageABI)) + if err != nil { + return nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(DecisionStorageSMBin) + } else { + bytecode = common.FromHex(DecisionStorageBin) + } + if len(bytecode) == 0 { + return nil, fmt.Errorf("cannot deploy empty bytecode") + } + tx, err := bind.AsyncDeployContract(auth, handler, parsed, bytecode, DecisionStorageABI, backend) + if err != nil { + return nil, err + } + return tx, nil +} + +// DecisionStorage is an auto generated Go binding around a Solidity contract. +type DecisionStorage struct { + DecisionStorageCaller // Read-only binding to the contract + DecisionStorageTransactor // Write-only binding to the contract + DecisionStorageFilterer // Log filterer for contract events +} + +// DecisionStorageCaller is an auto generated read-only Go binding around a Solidity contract. +type DecisionStorageCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DecisionStorageTransactor is an auto generated write-only Go binding around a Solidity contract. +type DecisionStorageTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DecisionStorageFilterer is an auto generated log filtering Go binding around a Solidity contract events. +type DecisionStorageFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DecisionStorageSession is an auto generated Go binding around a Solidity contract, +// with pre-set call and transact options. +type DecisionStorageSession struct { + Contract *DecisionStorage // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DecisionStorageCallerSession is an auto generated read-only Go binding around a Solidity contract, +// with pre-set call options. +type DecisionStorageCallerSession struct { + Contract *DecisionStorageCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// DecisionStorageTransactorSession is an auto generated write-only Go binding around a Solidity contract, +// with pre-set transact options. +type DecisionStorageTransactorSession struct { + Contract *DecisionStorageTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DecisionStorageRaw is an auto generated low-level Go binding around a Solidity contract. +type DecisionStorageRaw struct { + Contract *DecisionStorage // Generic contract binding to access the raw methods on +} + +// DecisionStorageCallerRaw is an auto generated low-level read-only Go binding around a Solidity contract. +type DecisionStorageCallerRaw struct { + Contract *DecisionStorageCaller // Generic read-only contract binding to access the raw methods on +} + +// DecisionStorageTransactorRaw is an auto generated low-level write-only Go binding around a Solidity contract. +type DecisionStorageTransactorRaw struct { + Contract *DecisionStorageTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewDecisionStorage creates a new instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorage(address common.Address, backend bind.ContractBackend) (*DecisionStorage, error) { + contract, err := bindDecisionStorage(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DecisionStorage{DecisionStorageCaller: DecisionStorageCaller{contract: contract}, DecisionStorageTransactor: DecisionStorageTransactor{contract: contract}, DecisionStorageFilterer: DecisionStorageFilterer{contract: contract}}, nil +} + +// NewDecisionStorageCaller creates a new read-only instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorageCaller(address common.Address, caller bind.ContractCaller) (*DecisionStorageCaller, error) { + contract, err := bindDecisionStorage(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DecisionStorageCaller{contract: contract}, nil +} + +// NewDecisionStorageTransactor creates a new write-only instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorageTransactor(address common.Address, transactor bind.ContractTransactor) (*DecisionStorageTransactor, error) { + contract, err := bindDecisionStorage(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DecisionStorageTransactor{contract: contract}, nil +} + +// NewDecisionStorageFilterer creates a new log filterer instance of DecisionStorage, bound to a specific deployed contract. +func NewDecisionStorageFilterer(address common.Address, filterer bind.ContractFilterer) (*DecisionStorageFilterer, error) { + contract, err := bindDecisionStorage(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DecisionStorageFilterer{contract: contract}, nil +} + +// bindDecisionStorage binds a generic wrapper to an already deployed contract. +func bindDecisionStorage(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(DecisionStorageABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_DecisionStorage *DecisionStorageRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _DecisionStorage.Contract.DecisionStorageCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_DecisionStorage *DecisionStorageRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.DecisionStorageTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_DecisionStorage *DecisionStorageRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.DecisionStorageTransactor.contract.TransactWithResult(opts, result, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_DecisionStorage *DecisionStorageCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _DecisionStorage.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_DecisionStorage *DecisionStorageTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_DecisionStorage *DecisionStorageTransactorRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.contract.TransactWithResult(opts, result, method, params...) +} + +// Decisions is a free data retrieval call binding the contract method 0xb829df82. +// +// Solidity: function decisions(uint256 ) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCaller) Decisions(opts *bind.CallOpts, arg0 *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + ret := new(struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool + }) + out := ret + err := _DecisionStorage.contract.Call(opts, out, "decisions", arg0) + return *ret, err +} + +// Decisions is a free data retrieval call binding the contract method 0xb829df82. +// +// Solidity: function decisions(uint256 ) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) Decisions(arg0 *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.Decisions(&_DecisionStorage.CallOpts, arg0) +} + +// Decisions is a free data retrieval call binding the contract method 0xb829df82. +// +// Solidity: function decisions(uint256 ) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCallerSession) Decisions(arg0 *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.Decisions(&_DecisionStorage.CallOpts, arg0) +} + +// GetDecision is a free data retrieval call binding the contract method 0xaf754848. +// +// Solidity: function getDecision(uint256 index) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCaller) GetDecision(opts *bind.CallOpts, index *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + ret := new(struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool + }) + out := ret + err := _DecisionStorage.contract.Call(opts, out, "getDecision", index) + return *ret, err +} + +// GetDecision is a free data retrieval call binding the contract method 0xaf754848. +// +// Solidity: function getDecision(uint256 index) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) GetDecision(index *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.GetDecision(&_DecisionStorage.CallOpts, index) +} + +// GetDecision is a free data retrieval call binding the contract method 0xaf754848. +// +// Solidity: function getDecision(uint256 index) constant returns(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageCallerSession) GetDecision(index *big.Int) (struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool +}, error) { + return _DecisionStorage.Contract.GetDecision(&_DecisionStorage.CallOpts, index) +} + +// GetDecisionCount is a free data retrieval call binding the contract method 0xe4ff19da. +// +// Solidity: function getDecisionCount() constant returns(uint256) +func (_DecisionStorage *DecisionStorageCaller) GetDecisionCount(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _DecisionStorage.contract.Call(opts, out, "getDecisionCount") + return *ret0, err +} + +// GetDecisionCount is a free data retrieval call binding the contract method 0xe4ff19da. +// +// Solidity: function getDecisionCount() constant returns(uint256) +func (_DecisionStorage *DecisionStorageSession) GetDecisionCount() (*big.Int, error) { + return _DecisionStorage.Contract.GetDecisionCount(&_DecisionStorage.CallOpts) +} + +// GetDecisionCount is a free data retrieval call binding the contract method 0xe4ff19da. +// +// Solidity: function getDecisionCount() constant returns(uint256) +func (_DecisionStorage *DecisionStorageCallerSession) GetDecisionCount() (*big.Int, error) { + return _DecisionStorage.Contract.GetDecisionCount(&_DecisionStorage.CallOpts) +} + +// VerifyEvidence is a free data retrieval call binding the contract method 0x6f13f95a. +// +// Solidity: function verifyEvidence(string evidence) constant returns(bool) +func (_DecisionStorage *DecisionStorageCaller) VerifyEvidence(opts *bind.CallOpts, evidence string) (bool, error) { + var ( + ret0 = new(bool) + ) + out := ret0 + err := _DecisionStorage.contract.Call(opts, out, "verifyEvidence", evidence) + return *ret0, err +} + +// VerifyEvidence is a free data retrieval call binding the contract method 0x6f13f95a. +// +// Solidity: function verifyEvidence(string evidence) constant returns(bool) +func (_DecisionStorage *DecisionStorageSession) VerifyEvidence(evidence string) (bool, error) { + return _DecisionStorage.Contract.VerifyEvidence(&_DecisionStorage.CallOpts, evidence) +} + +// VerifyEvidence is a free data retrieval call binding the contract method 0x6f13f95a. +// +// Solidity: function verifyEvidence(string evidence) constant returns(bool) +func (_DecisionStorage *DecisionStorageCallerSession) VerifyEvidence(evidence string) (bool, error) { + return _DecisionStorage.Contract.VerifyEvidence(&_DecisionStorage.CallOpts, evidence) +} + +// RecordDecision is a paid mutator transaction binding the contract method 0xb8c0dbbd. +// +// Solidity: function recordDecision(string evidence, string nodeID, bool approved) returns(bool) +func (_DecisionStorage *DecisionStorageTransactor) RecordDecision(opts *bind.TransactOpts, evidence string, nodeID string, approved bool) (bool, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(bool) + ) + out := ret0 + transaction, receipt, err := _DecisionStorage.contract.TransactWithResult(opts, out, "recordDecision", evidence, nodeID, approved) + return *ret0, transaction, receipt, err +} + +func (_DecisionStorage *DecisionStorageTransactor) AsyncRecordDecision(handler func(*types.Receipt, error), opts *bind.TransactOpts, evidence string, nodeID string, approved bool) (*types.Transaction, error) { + return _DecisionStorage.contract.AsyncTransact(opts, handler, "recordDecision", evidence, nodeID, approved) +} + +// RecordDecision is a paid mutator transaction binding the contract method 0xb8c0dbbd. +// +// Solidity: function recordDecision(string evidence, string nodeID, bool approved) returns(bool) +func (_DecisionStorage *DecisionStorageSession) RecordDecision(evidence string, nodeID string, approved bool) (bool, *types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.RecordDecision(&_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +func (_DecisionStorage *DecisionStorageSession) AsyncRecordDecision(handler func(*types.Receipt, error), evidence string, nodeID string, approved bool) (*types.Transaction, error) { + return _DecisionStorage.Contract.AsyncRecordDecision(handler, &_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +// RecordDecision is a paid mutator transaction binding the contract method 0xb8c0dbbd. +// +// Solidity: function recordDecision(string evidence, string nodeID, bool approved) returns(bool) +func (_DecisionStorage *DecisionStorageTransactorSession) RecordDecision(evidence string, nodeID string, approved bool) (bool, *types.Transaction, *types.Receipt, error) { + return _DecisionStorage.Contract.RecordDecision(&_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +func (_DecisionStorage *DecisionStorageTransactorSession) AsyncRecordDecision(handler func(*types.Receipt, error), evidence string, nodeID string, approved bool) (*types.Transaction, error) { + return _DecisionStorage.Contract.AsyncRecordDecision(handler, &_DecisionStorage.TransactOpts, evidence, nodeID, approved) +} + +// DecisionStorageDecisionRecorded represents a DecisionRecorded event raised by the DecisionStorage contract. +type DecisionStorageDecisionRecorded struct { + Evidence string + NodeID string + Timestamp *big.Int + Approved bool + Raw types.Log // Blockchain specific contextual infos +} + +// WatchDecisionRecorded is a free log subscription operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageFilterer) WatchDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.contract.WatchLogs(fromBlock, handler, "DecisionRecorded") +} + +func (_DecisionStorage *DecisionStorageFilterer) WatchAllDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.contract.WatchLogs(fromBlock, handler, "DecisionRecorded") +} + +// ParseDecisionRecorded is a log parse operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageFilterer) ParseDecisionRecorded(log types.Log) (*DecisionStorageDecisionRecorded, error) { + event := new(DecisionStorageDecisionRecorded) + if err := _DecisionStorage.contract.UnpackLog(event, "DecisionRecorded", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchDecisionRecorded is a free log subscription operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) WatchDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.Contract.WatchDecisionRecorded(fromBlock, handler) +} + +func (_DecisionStorage *DecisionStorageSession) WatchAllDecisionRecorded(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _DecisionStorage.Contract.WatchAllDecisionRecorded(fromBlock, handler) +} + +// ParseDecisionRecorded is a log parse operation binding the contract event 0x641014cc6abca52c1d84346d29137b54af42068d1b3b5b3c52e98b16eb085428. +// +// Solidity: event DecisionRecorded(string evidence, string nodeID, uint256 timestamp, bool approved) +func (_DecisionStorage *DecisionStorageSession) ParseDecisionRecorded(log types.Log) (*DecisionStorageDecisionRecorded, error) { + return _DecisionStorage.Contract.ParseDecisionRecorded(log) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.sol new file mode 100644 index 000000000..19c90c817 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/Decision/DecisionStorage.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.10 <0.8.20; + +contract DecisionStorage { + // 决策记录结构 + struct Decision { + string evidence; // 证据 + string nodeID; // 节点ID + uint256 timestamp; // 时间戳 + bool approved; // 是否批准 + } + + // 存储所有决策记录 + Decision[] public decisions; + + // 记录决策的事件 + event DecisionRecorded( + string evidence, + string nodeID, + uint256 timestamp, + bool approved + ); + + function recordDecision(string memory evidence, string memory nodeID, bool approved) public returns (bool) { + Decision memory newDecision = Decision({ + evidence: evidence, + nodeID: nodeID, + timestamp: block.timestamp, + approved: approved + }); + + // 存储决策 + decisions.push(newDecision); + + // 触发事件 + emit DecisionRecorded( + newDecision.evidence, + newDecision.nodeID, + newDecision.timestamp, + newDecision.approved + ); + + return true; + } + + // 验证证据 可以添加具体的验证逻辑 + function verifyEvidence(string memory evidence) public view returns (bool) { + // 目前返回true用于测试 + return true; + } + + // 获取决策记录数量 + function getDecisionCount() public view returns (uint256) { + return decisions.length; + } + + // 获取指定索引的决策记录 + function getDecision(uint256 index) public view returns ( + string memory evidence, + string memory nodeID, + uint256 timestamp, + bool approved + ) { + require(index < decisions.length, "Index out of bounds"); + Decision memory decision = decisions[index]; + return ( + decision.evidence, + decision.nodeID, + decision.timestamp, + decision.approved + ); + } + +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KVTable.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KVTable.abi new file mode 100644 index 000000000..134580fc6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KVTable.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"get","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"set","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KVTable.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KVTable.bin new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.abi new file mode 100644 index 000000000..1103d5e2d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"CreateResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"InsertResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"RemoveResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"count","type":"int256"}],"name":"UpdateResult","type":"event"},{"inputs":[{"internalType":"string","name":"tableName","type":"string"},{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"name":"createTable","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"desc","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"secret","type":"string"}],"name":"insert","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"}],"name":"remove","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"}],"name":"select","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"secret","type":"string"}],"name":"update","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.bin new file mode 100644 index 000000000..55e893525 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b506000600267ffffffffffffffff81111562000032576200003162000399565b5b6040519080825280602002602001820160405280156200006757816020015b6060815260200190600190039081620000515790505b5090506040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525081600081518110620000b757620000b6620003c8565b5b60200260200101819052506040518060400160405280600681526020017f7365637265740000000000000000000000000000000000000000000000000000815250816001815181106200010f576200010e620003c8565b5b6020026020010181905250600060405180604001604052806040518060400160405280600281526020017f6964000000000000000000000000000000000000000000000000000000000000815250815260200183815250905061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e6040518060400160405280600b81526020017f745f6b65795f7368617265000000000000000000000000000000000000000000815250836040518363ffffffff1660e01b8152600401620001dc92919062000605565b6020604051808303816000875af1158015620001fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000222919062000683565b50600061100273ffffffffffffffffffffffffffffffffffffffff1663f23f63c96040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b8152600401620002979190620006b5565b602060405180830381865afa158015620002b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002db91906200073e565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000347906200079a565b60405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050620007bc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081519050919050565b600082825260208201905092915050565b60005b838110156200043357808201518184015260208101905062000416565b8381111562000443576000848401525b50505050565b6000601f19601f8301169050919050565b60006200046782620003f7565b62000473818562000402565b93506200048581856020860162000413565b620004908162000449565b840191505092915050565b600082825260208201905092915050565b6000620004b982620003f7565b620004c581856200049b565b9350620004d781856020860162000413565b620004e28162000449565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000620005278383620004ac565b905092915050565b6000602082019050919050565b60006200054982620004ed565b620005558185620004f8565b935083602082028501620005698562000509565b8060005b85811015620005ab578484038952815162000589858262000519565b945062000596836200052f565b925060208a019950506001810190506200056d565b50829750879550505050505092915050565b60006040830160008301518482036000860152620005dc8282620004ac565b91505060208301518482036020860152620005f882826200053c565b9150508091505092915050565b600060408201905081810360008301526200062181856200045a565b90508181036020830152620006378184620005bd565b90509392505050565b600080fd5b60008160030b9050919050565b6200065d8162000645565b81146200066957600080fd5b50565b6000815190506200067d8162000652565b92915050565b6000602082840312156200069c576200069b62000640565b5b6000620006ac848285016200066c565b91505092915050565b60006020820190508181036000830152620006d181846200045a565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200070682620006d9565b9050919050565b6200071881620006f9565b81146200072457600080fd5b50565b60008151905062000738816200070d565b92915050565b60006020828403121562000757576200075662000640565b5b6000620007678482850162000727565b91505092915050565b50565b60006200078260008362000402565b91506200078f8262000770565b600082019050919050565b60006020820190508181036000830152620007b58162000773565b9050919050565b6115c480620007cc6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80632fe99bdc1461006757806331c3e4561461009757806355f150f1146100c75780636a5bae4e146100e657806380599e4b14610116578063fcd7e3c114610146575b600080fd5b610081600480360381019061007c9190610a2f565b610177565b60405161008e9190610af2565b60405180910390f35b6100b160048036038101906100ac9190610a2f565b610309565b6040516100be9190610af2565b60405180910390f35b6100cf61051b565b6040516100dd929190610ca1565b60405180910390f35b61010060048036038101906100fb9190610dbe565b6105eb565b60405161010d9190610e7e565b60405180910390f35b610130600480360381019061012b9190610e99565b6106cf565b60405161013d9190610af2565b60405180910390f35b610160600480360381019061015b9190610e99565b6107b0565b60405161016e929190610ee2565b60405180910390f35b600080600267ffffffffffffffff81111561019557610194610904565b5b6040519080825280602002602001820160405280156101c857816020015b60608152602001906001900390816101b35790505b50905083816000815181106101e0576101df610f19565b5b60200260200101819052508281600181518110610200576101ff610f19565b5b60200260200101819052506000604051806040016040528087815260200183815250905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635c6e105f836040518263ffffffff1660e01b81526004016102809190611012565b6020604051808303816000875af115801561029f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c39190611060565b90507fc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce816040516102f491906110c8565b60405180910390a18093505050509392505050565b600080600267ffffffffffffffff81111561032757610326610904565b5b60405190808252806020026020018201604052801561036057816020015b61034d6108bb565b8152602001906001900390816103455790505b50905060405180604001604052806040518060400160405280600481526020017f6e616d6500000000000000000000000000000000000000000000000000000000815250815260200185815250816000815181106103c1576103c0610f19565b5b602002602001018190525060405180604001604052806040518060400160405280600681526020017f73656372657400000000000000000000000000000000000000000000000000008152508152602001848152508160018151811061042a57610429610f19565b5b602002602001018190525060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166341ffd75f87846040518363ffffffff1660e01b81526004016104939291906111e9565b6020604051808303816000875af11580156104b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d69190611060565b90507f8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a8160405161050791906110c8565b60405180910390a180925050509392505050565b606080600061100273ffffffffffffffffffffffffffffffffffffffff16635d0d6d546040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016105909190611220565b600060405180830381865afa1580156105ad573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906105d691906113f9565b90508060000151816020015192509250509091565b6000806040518060400160405280858152602001848152509050600061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e87846040518363ffffffff1660e01b8152600401610644929190611486565b6020604051808303816000875af1158015610663573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106879190611060565b90507fb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210816040516106b891906110c8565b60405180910390a18060030b925050509392505050565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166380599e4b846040518263ffffffff1660e01b815260040161072b9190611220565b6020604051808303816000875af115801561074a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076e9190611060565b90507f4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc31573581128160405161079f91906110c8565b60405180910390a180915050919050565b60608060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fcd7e3c1856040518263ffffffff1660e01b815260040161080f9190611220565b600060405180830381865afa15801561082c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906108559190611545565b9050606080600283602001515114156108ad5782602001516000815181106108805761087f610f19565b5b6020026020010151915082602001516001815181106108a2576108a1610f19565b5b602002602001015190505b818194509450505050915091565b604051806040016040528060608152602001606081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61093c826108f3565b810181811067ffffffffffffffff8211171561095b5761095a610904565b5b80604052505050565b600061096e6108d5565b905061097a8282610933565b919050565b600067ffffffffffffffff82111561099a57610999610904565b5b6109a3826108f3565b9050602081019050919050565b82818337600083830152505050565b60006109d26109cd8461097f565b610964565b9050828152602081018484840111156109ee576109ed6108ee565b5b6109f98482856109b0565b509392505050565b600082601f830112610a1657610a156108e9565b5b8135610a268482602086016109bf565b91505092915050565b600080600060608486031215610a4857610a476108df565b5b600084013567ffffffffffffffff811115610a6657610a656108e4565b5b610a7286828701610a01565b935050602084013567ffffffffffffffff811115610a9357610a926108e4565b5b610a9f86828701610a01565b925050604084013567ffffffffffffffff811115610ac057610abf6108e4565b5b610acc86828701610a01565b9150509250925092565b60008160030b9050919050565b610aec81610ad6565b82525050565b6000602082019050610b076000830184610ae3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b47578082015181840152602081019050610b2c565b83811115610b56576000848401525b50505050565b6000610b6782610b0d565b610b718185610b18565b9350610b81818560208601610b29565b610b8a816108f3565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600082825260208201905092915050565b6000610bdd82610b0d565b610be78185610bc1565b9350610bf7818560208601610b29565b610c00816108f3565b840191505092915050565b6000610c178383610bd2565b905092915050565b6000602082019050919050565b6000610c3782610b95565b610c418185610ba0565b935083602082028501610c5385610bb1565b8060005b85811015610c8f5784840389528151610c708582610c0b565b9450610c7b83610c1f565b925060208a01995050600181019050610c57565b50829750879550505050505092915050565b60006040820190508181036000830152610cbb8185610b5c565b90508181036020830152610ccf8184610c2c565b90509392505050565b600067ffffffffffffffff821115610cf357610cf2610904565b5b602082029050602081019050919050565b600080fd5b6000610d1c610d1784610cd8565b610964565b90508083825260208201905060208402830185811115610d3f57610d3e610d04565b5b835b81811015610d8657803567ffffffffffffffff811115610d6457610d636108e9565b5b808601610d718982610a01565b85526020850194505050602081019050610d41565b5050509392505050565b600082601f830112610da557610da46108e9565b5b8135610db5848260208601610d09565b91505092915050565b600080600060608486031215610dd757610dd66108df565b5b600084013567ffffffffffffffff811115610df557610df46108e4565b5b610e0186828701610a01565b935050602084013567ffffffffffffffff811115610e2257610e216108e4565b5b610e2e86828701610a01565b925050604084013567ffffffffffffffff811115610e4f57610e4e6108e4565b5b610e5b86828701610d90565b9150509250925092565b6000819050919050565b610e7881610e65565b82525050565b6000602082019050610e936000830184610e6f565b92915050565b600060208284031215610eaf57610eae6108df565b5b600082013567ffffffffffffffff811115610ecd57610ecc6108e4565b5b610ed984828501610a01565b91505092915050565b60006040820190508181036000830152610efc8185610b5c565b90508181036020830152610f108184610b5c565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b6000610f6482610b95565b610f6e8185610f48565b935083602082028501610f8085610bb1565b8060005b85811015610fbc5784840389528151610f9d8582610c0b565b9450610fa883610c1f565b925060208a01995050600181019050610f84565b50829750879550505050505092915050565b60006040830160008301518482036000860152610feb8282610bd2565b915050602083015184820360208601526110058282610f59565b9150508091505092915050565b6000602082019050818103600083015261102c8184610fce565b905092915050565b61103d81610ad6565b811461104857600080fd5b50565b60008151905061105a81611034565b92915050565b600060208284031215611076576110756108df565b5b60006110848482850161104b565b91505092915050565b6000819050919050565b60006110b26110ad6110a884610ad6565b61108d565b610e65565b9050919050565b6110c281611097565b82525050565b60006020820190506110dd60008301846110b9565b92915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000604083016000830151848203600086015261112c8282610bd2565b915050602083015184820360208601526111468282610bd2565b9150508091505092915050565b600061115f838361110f565b905092915050565b6000602082019050919050565b600061117f826110e3565b61118981856110ee565b93508360208202850161119b856110ff565b8060005b858110156111d757848403895281516111b88582611153565b94506111c383611167565b925060208a0199505060018101905061119f565b50829750879550505050505092915050565b600060408201905081810360008301526112038185610b5c565b905081810360208301526112178184611174565b90509392505050565b6000602082019050818103600083015261123a8184610b5c565b905092915050565b600080fd5b600080fd5b600061125f61125a8461097f565b610964565b90508281526020810184848401111561127b5761127a6108ee565b5b611286848285610b29565b509392505050565b600082601f8301126112a3576112a26108e9565b5b81516112b384826020860161124c565b91505092915050565b60006112cf6112ca84610cd8565b610964565b905080838252602082019050602084028301858111156112f2576112f1610d04565b5b835b8181101561133957805167ffffffffffffffff811115611317576113166108e9565b5b808601611324898261128e565b855260208501945050506020810190506112f4565b5050509392505050565b600082601f830112611358576113576108e9565b5b81516113688482602086016112bc565b91505092915050565b60006040828403121561138757611386611242565b5b6113916040610964565b9050600082015167ffffffffffffffff8111156113b1576113b0611247565b5b6113bd8482850161128e565b600083015250602082015167ffffffffffffffff8111156113e1576113e0611247565b5b6113ed84828501611343565b60208301525092915050565b60006020828403121561140f5761140e6108df565b5b600082015167ffffffffffffffff81111561142d5761142c6108e4565b5b61143984828501611371565b91505092915050565b6000604083016000830151848203600086015261145f8282610bd2565b915050602083015184820360208601526114798282610f59565b9150508091505092915050565b600060408201905081810360008301526114a08185610b5c565b905081810360208301526114b48184611442565b90509392505050565b6000604082840312156114d3576114d2611242565b5b6114dd6040610964565b9050600082015167ffffffffffffffff8111156114fd576114fc611247565b5b6115098482850161128e565b600083015250602082015167ffffffffffffffff81111561152d5761152c611247565b5b61153984828501611343565b60208301525092915050565b60006020828403121561155b5761155a6108df565b5b600082015167ffffffffffffffff811115611579576115786108e4565b5b611585848285016114bd565b9150509291505056fea2646970667358221220c4aba62bac51140354dd55b475ed3bde2226a18fc413bccf0b2fbfb1fec7c8dc64736f6c634300080b0033 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.go new file mode 100644 index 000000000..21e5ab5b6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.go @@ -0,0 +1,614 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package KeyShare + +import ( + "fmt" + "math/big" + "strings" + + "github.com/FISCO-BCOS/go-sdk/v3/abi" + "github.com/FISCO-BCOS/go-sdk/v3/abi/bind" + "github.com/FISCO-BCOS/go-sdk/v3/types" + "github.com/ethereum/go-ethereum/common" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = abi.U256 + _ = bind.Bind + _ = common.Big1 +) + +// KeyShareABI is the input ABI used to generate the binding from. +const KeyShareABI = "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"CreateResult\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"InsertResult\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"RemoveResult\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"count\",\"type\":\"int256\"}],\"name\":\"UpdateResult\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"tableName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"fields\",\"type\":\"string[]\"}],\"name\":\"createTable\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"desc\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"secret\",\"type\":\"string\"}],\"name\":\"insert\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"remove\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"select\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"secret\",\"type\":\"string\"}],\"name\":\"update\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// KeyShareBin is the compiled bytecode used for deploying new contracts. +var KeyShareBin = "0x60806040523480156200001157600080fd5b506000600267ffffffffffffffff81111562000032576200003162000399565b5b6040519080825280602002602001820160405280156200006757816020015b6060815260200190600190039081620000515790505b5090506040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525081600081518110620000b757620000b6620003c8565b5b60200260200101819052506040518060400160405280600681526020017f7365637265740000000000000000000000000000000000000000000000000000815250816001815181106200010f576200010e620003c8565b5b6020026020010181905250600060405180604001604052806040518060400160405280600281526020017f6964000000000000000000000000000000000000000000000000000000000000815250815260200183815250905061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e6040518060400160405280600b81526020017f745f6b65795f7368617265000000000000000000000000000000000000000000815250836040518363ffffffff1660e01b8152600401620001dc92919062000605565b6020604051808303816000875af1158015620001fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000222919062000683565b50600061100273ffffffffffffffffffffffffffffffffffffffff1663f23f63c96040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b8152600401620002979190620006b5565b602060405180830381865afa158015620002b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002db91906200073e565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000347906200079a565b60405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050620007bc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081519050919050565b600082825260208201905092915050565b60005b838110156200043357808201518184015260208101905062000416565b8381111562000443576000848401525b50505050565b6000601f19601f8301169050919050565b60006200046782620003f7565b62000473818562000402565b93506200048581856020860162000413565b620004908162000449565b840191505092915050565b600082825260208201905092915050565b6000620004b982620003f7565b620004c581856200049b565b9350620004d781856020860162000413565b620004e28162000449565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000620005278383620004ac565b905092915050565b6000602082019050919050565b60006200054982620004ed565b620005558185620004f8565b935083602082028501620005698562000509565b8060005b85811015620005ab578484038952815162000589858262000519565b945062000596836200052f565b925060208a019950506001810190506200056d565b50829750879550505050505092915050565b60006040830160008301518482036000860152620005dc8282620004ac565b91505060208301518482036020860152620005f882826200053c565b9150508091505092915050565b600060408201905081810360008301526200062181856200045a565b90508181036020830152620006378184620005bd565b90509392505050565b600080fd5b60008160030b9050919050565b6200065d8162000645565b81146200066957600080fd5b50565b6000815190506200067d8162000652565b92915050565b6000602082840312156200069c576200069b62000640565b5b6000620006ac848285016200066c565b91505092915050565b60006020820190508181036000830152620006d181846200045a565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200070682620006d9565b9050919050565b6200071881620006f9565b81146200072457600080fd5b50565b60008151905062000738816200070d565b92915050565b60006020828403121562000757576200075662000640565b5b6000620007678482850162000727565b91505092915050565b50565b60006200078260008362000402565b91506200078f8262000770565b600082019050919050565b60006020820190508181036000830152620007b58162000773565b9050919050565b6115c480620007cc6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80632fe99bdc1461006757806331c3e4561461009757806355f150f1146100c75780636a5bae4e146100e657806380599e4b14610116578063fcd7e3c114610146575b600080fd5b610081600480360381019061007c9190610a2f565b610177565b60405161008e9190610af2565b60405180910390f35b6100b160048036038101906100ac9190610a2f565b610309565b6040516100be9190610af2565b60405180910390f35b6100cf61051b565b6040516100dd929190610ca1565b60405180910390f35b61010060048036038101906100fb9190610dbe565b6105eb565b60405161010d9190610e7e565b60405180910390f35b610130600480360381019061012b9190610e99565b6106cf565b60405161013d9190610af2565b60405180910390f35b610160600480360381019061015b9190610e99565b6107b0565b60405161016e929190610ee2565b60405180910390f35b600080600267ffffffffffffffff81111561019557610194610904565b5b6040519080825280602002602001820160405280156101c857816020015b60608152602001906001900390816101b35790505b50905083816000815181106101e0576101df610f19565b5b60200260200101819052508281600181518110610200576101ff610f19565b5b60200260200101819052506000604051806040016040528087815260200183815250905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635c6e105f836040518263ffffffff1660e01b81526004016102809190611012565b6020604051808303816000875af115801561029f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c39190611060565b90507fc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce816040516102f491906110c8565b60405180910390a18093505050509392505050565b600080600267ffffffffffffffff81111561032757610326610904565b5b60405190808252806020026020018201604052801561036057816020015b61034d6108bb565b8152602001906001900390816103455790505b50905060405180604001604052806040518060400160405280600481526020017f6e616d6500000000000000000000000000000000000000000000000000000000815250815260200185815250816000815181106103c1576103c0610f19565b5b602002602001018190525060405180604001604052806040518060400160405280600681526020017f73656372657400000000000000000000000000000000000000000000000000008152508152602001848152508160018151811061042a57610429610f19565b5b602002602001018190525060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166341ffd75f87846040518363ffffffff1660e01b81526004016104939291906111e9565b6020604051808303816000875af11580156104b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d69190611060565b90507f8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a8160405161050791906110c8565b60405180910390a180925050509392505050565b606080600061100273ffffffffffffffffffffffffffffffffffffffff16635d0d6d546040518060400160405280600b81526020017f745f6b65795f73686172650000000000000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016105909190611220565b600060405180830381865afa1580156105ad573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906105d691906113f9565b90508060000151816020015192509250509091565b6000806040518060400160405280858152602001848152509050600061100273ffffffffffffffffffffffffffffffffffffffff166331a5a51e87846040518363ffffffff1660e01b8152600401610644929190611486565b6020604051808303816000875af1158015610663573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106879190611060565b90507fb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210816040516106b891906110c8565b60405180910390a18060030b925050509392505050565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166380599e4b846040518263ffffffff1660e01b815260040161072b9190611220565b6020604051808303816000875af115801561074a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076e9190611060565b90507f4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc31573581128160405161079f91906110c8565b60405180910390a180915050919050565b60608060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fcd7e3c1856040518263ffffffff1660e01b815260040161080f9190611220565b600060405180830381865afa15801561082c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906108559190611545565b9050606080600283602001515114156108ad5782602001516000815181106108805761087f610f19565b5b6020026020010151915082602001516001815181106108a2576108a1610f19565b5b602002602001015190505b818194509450505050915091565b604051806040016040528060608152602001606081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61093c826108f3565b810181811067ffffffffffffffff8211171561095b5761095a610904565b5b80604052505050565b600061096e6108d5565b905061097a8282610933565b919050565b600067ffffffffffffffff82111561099a57610999610904565b5b6109a3826108f3565b9050602081019050919050565b82818337600083830152505050565b60006109d26109cd8461097f565b610964565b9050828152602081018484840111156109ee576109ed6108ee565b5b6109f98482856109b0565b509392505050565b600082601f830112610a1657610a156108e9565b5b8135610a268482602086016109bf565b91505092915050565b600080600060608486031215610a4857610a476108df565b5b600084013567ffffffffffffffff811115610a6657610a656108e4565b5b610a7286828701610a01565b935050602084013567ffffffffffffffff811115610a9357610a926108e4565b5b610a9f86828701610a01565b925050604084013567ffffffffffffffff811115610ac057610abf6108e4565b5b610acc86828701610a01565b9150509250925092565b60008160030b9050919050565b610aec81610ad6565b82525050565b6000602082019050610b076000830184610ae3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b47578082015181840152602081019050610b2c565b83811115610b56576000848401525b50505050565b6000610b6782610b0d565b610b718185610b18565b9350610b81818560208601610b29565b610b8a816108f3565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600082825260208201905092915050565b6000610bdd82610b0d565b610be78185610bc1565b9350610bf7818560208601610b29565b610c00816108f3565b840191505092915050565b6000610c178383610bd2565b905092915050565b6000602082019050919050565b6000610c3782610b95565b610c418185610ba0565b935083602082028501610c5385610bb1565b8060005b85811015610c8f5784840389528151610c708582610c0b565b9450610c7b83610c1f565b925060208a01995050600181019050610c57565b50829750879550505050505092915050565b60006040820190508181036000830152610cbb8185610b5c565b90508181036020830152610ccf8184610c2c565b90509392505050565b600067ffffffffffffffff821115610cf357610cf2610904565b5b602082029050602081019050919050565b600080fd5b6000610d1c610d1784610cd8565b610964565b90508083825260208201905060208402830185811115610d3f57610d3e610d04565b5b835b81811015610d8657803567ffffffffffffffff811115610d6457610d636108e9565b5b808601610d718982610a01565b85526020850194505050602081019050610d41565b5050509392505050565b600082601f830112610da557610da46108e9565b5b8135610db5848260208601610d09565b91505092915050565b600080600060608486031215610dd757610dd66108df565b5b600084013567ffffffffffffffff811115610df557610df46108e4565b5b610e0186828701610a01565b935050602084013567ffffffffffffffff811115610e2257610e216108e4565b5b610e2e86828701610a01565b925050604084013567ffffffffffffffff811115610e4f57610e4e6108e4565b5b610e5b86828701610d90565b9150509250925092565b6000819050919050565b610e7881610e65565b82525050565b6000602082019050610e936000830184610e6f565b92915050565b600060208284031215610eaf57610eae6108df565b5b600082013567ffffffffffffffff811115610ecd57610ecc6108e4565b5b610ed984828501610a01565b91505092915050565b60006040820190508181036000830152610efc8185610b5c565b90508181036020830152610f108184610b5c565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b6000610f6482610b95565b610f6e8185610f48565b935083602082028501610f8085610bb1565b8060005b85811015610fbc5784840389528151610f9d8582610c0b565b9450610fa883610c1f565b925060208a01995050600181019050610f84565b50829750879550505050505092915050565b60006040830160008301518482036000860152610feb8282610bd2565b915050602083015184820360208601526110058282610f59565b9150508091505092915050565b6000602082019050818103600083015261102c8184610fce565b905092915050565b61103d81610ad6565b811461104857600080fd5b50565b60008151905061105a81611034565b92915050565b600060208284031215611076576110756108df565b5b60006110848482850161104b565b91505092915050565b6000819050919050565b60006110b26110ad6110a884610ad6565b61108d565b610e65565b9050919050565b6110c281611097565b82525050565b60006020820190506110dd60008301846110b9565b92915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000604083016000830151848203600086015261112c8282610bd2565b915050602083015184820360208601526111468282610bd2565b9150508091505092915050565b600061115f838361110f565b905092915050565b6000602082019050919050565b600061117f826110e3565b61118981856110ee565b93508360208202850161119b856110ff565b8060005b858110156111d757848403895281516111b88582611153565b94506111c383611167565b925060208a0199505060018101905061119f565b50829750879550505050505092915050565b600060408201905081810360008301526112038185610b5c565b905081810360208301526112178184611174565b90509392505050565b6000602082019050818103600083015261123a8184610b5c565b905092915050565b600080fd5b600080fd5b600061125f61125a8461097f565b610964565b90508281526020810184848401111561127b5761127a6108ee565b5b611286848285610b29565b509392505050565b600082601f8301126112a3576112a26108e9565b5b81516112b384826020860161124c565b91505092915050565b60006112cf6112ca84610cd8565b610964565b905080838252602082019050602084028301858111156112f2576112f1610d04565b5b835b8181101561133957805167ffffffffffffffff811115611317576113166108e9565b5b808601611324898261128e565b855260208501945050506020810190506112f4565b5050509392505050565b600082601f830112611358576113576108e9565b5b81516113688482602086016112bc565b91505092915050565b60006040828403121561138757611386611242565b5b6113916040610964565b9050600082015167ffffffffffffffff8111156113b1576113b0611247565b5b6113bd8482850161128e565b600083015250602082015167ffffffffffffffff8111156113e1576113e0611247565b5b6113ed84828501611343565b60208301525092915050565b60006020828403121561140f5761140e6108df565b5b600082015167ffffffffffffffff81111561142d5761142c6108e4565b5b61143984828501611371565b91505092915050565b6000604083016000830151848203600086015261145f8282610bd2565b915050602083015184820360208601526114798282610f59565b9150508091505092915050565b600060408201905081810360008301526114a08185610b5c565b905081810360208301526114b48184611442565b90509392505050565b6000604082840312156114d3576114d2611242565b5b6114dd6040610964565b9050600082015167ffffffffffffffff8111156114fd576114fc611247565b5b6115098482850161128e565b600083015250602082015167ffffffffffffffff81111561152d5761152c611247565b5b61153984828501611343565b60208301525092915050565b60006020828403121561155b5761155a6108df565b5b600082015167ffffffffffffffff811115611579576115786108e4565b5b611585848285016114bd565b9150509291505056fea2646970667358221220c4aba62bac51140354dd55b475ed3bde2226a18fc413bccf0b2fbfb1fec7c8dc64736f6c634300080b0033" +var KeyShareSMBin = "0x" + +// DeployKeyShare deploys a new contract, binding an instance of KeyShare to it. +func DeployKeyShare(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Receipt, *KeyShare, error) { + parsed, err := abi.JSON(strings.NewReader(KeyShareABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(KeyShareSMBin) + } else { + bytecode = common.FromHex(KeyShareBin) + } + if len(bytecode) == 0 { + return common.Address{}, nil, nil, fmt.Errorf("cannot deploy empty bytecode") + } + address, receipt, contract, err := bind.DeployContract(auth, parsed, bytecode, KeyShareABI, backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, receipt, &KeyShare{KeyShareCaller: KeyShareCaller{contract: contract}, KeyShareTransactor: KeyShareTransactor{contract: contract}, KeyShareFilterer: KeyShareFilterer{contract: contract}}, nil +} + +func AsyncDeployKeyShare(auth *bind.TransactOpts, handler func(*types.Receipt, error), backend bind.ContractBackend) (*types.Transaction, error) { + parsed, err := abi.JSON(strings.NewReader(KeyShareABI)) + if err != nil { + return nil, err + } + + var bytecode []byte + if backend.SMCrypto() { + bytecode = common.FromHex(KeyShareSMBin) + } else { + bytecode = common.FromHex(KeyShareBin) + } + if len(bytecode) == 0 { + return nil, fmt.Errorf("cannot deploy empty bytecode") + } + tx, err := bind.AsyncDeployContract(auth, handler, parsed, bytecode, KeyShareABI, backend) + if err != nil { + return nil, err + } + return tx, nil +} + +// KeyShare is an auto generated Go binding around a Solidity contract. +type KeyShare struct { + KeyShareCaller // Read-only binding to the contract + KeyShareTransactor // Write-only binding to the contract + KeyShareFilterer // Log filterer for contract events +} + +// KeyShareCaller is an auto generated read-only Go binding around a Solidity contract. +type KeyShareCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// KeyShareTransactor is an auto generated write-only Go binding around a Solidity contract. +type KeyShareTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// KeyShareFilterer is an auto generated log filtering Go binding around a Solidity contract events. +type KeyShareFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// KeyShareSession is an auto generated Go binding around a Solidity contract, +// with pre-set call and transact options. +type KeyShareSession struct { + Contract *KeyShare // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// KeyShareCallerSession is an auto generated read-only Go binding around a Solidity contract, +// with pre-set call options. +type KeyShareCallerSession struct { + Contract *KeyShareCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// KeyShareTransactorSession is an auto generated write-only Go binding around a Solidity contract, +// with pre-set transact options. +type KeyShareTransactorSession struct { + Contract *KeyShareTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// KeyShareRaw is an auto generated low-level Go binding around a Solidity contract. +type KeyShareRaw struct { + Contract *KeyShare // Generic contract binding to access the raw methods on +} + +// KeyShareCallerRaw is an auto generated low-level read-only Go binding around a Solidity contract. +type KeyShareCallerRaw struct { + Contract *KeyShareCaller // Generic read-only contract binding to access the raw methods on +} + +// KeyShareTransactorRaw is an auto generated low-level write-only Go binding around a Solidity contract. +type KeyShareTransactorRaw struct { + Contract *KeyShareTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewKeyShare creates a new instance of KeyShare, bound to a specific deployed contract. +func NewKeyShare(address common.Address, backend bind.ContractBackend) (*KeyShare, error) { + contract, err := bindKeyShare(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &KeyShare{KeyShareCaller: KeyShareCaller{contract: contract}, KeyShareTransactor: KeyShareTransactor{contract: contract}, KeyShareFilterer: KeyShareFilterer{contract: contract}}, nil +} + +// NewKeyShareCaller creates a new read-only instance of KeyShare, bound to a specific deployed contract. +func NewKeyShareCaller(address common.Address, caller bind.ContractCaller) (*KeyShareCaller, error) { + contract, err := bindKeyShare(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &KeyShareCaller{contract: contract}, nil +} + +// NewKeyShareTransactor creates a new write-only instance of KeyShare, bound to a specific deployed contract. +func NewKeyShareTransactor(address common.Address, transactor bind.ContractTransactor) (*KeyShareTransactor, error) { + contract, err := bindKeyShare(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &KeyShareTransactor{contract: contract}, nil +} + +// NewKeyShareFilterer creates a new log filterer instance of KeyShare, bound to a specific deployed contract. +func NewKeyShareFilterer(address common.Address, filterer bind.ContractFilterer) (*KeyShareFilterer, error) { + contract, err := bindKeyShare(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &KeyShareFilterer{contract: contract}, nil +} + +// bindKeyShare binds a generic wrapper to an already deployed contract. +func bindKeyShare(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(KeyShareABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_KeyShare *KeyShareRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _KeyShare.Contract.KeyShareCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_KeyShare *KeyShareRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.KeyShareTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_KeyShare *KeyShareRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.KeyShareTransactor.contract.TransactWithResult(opts, result, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_KeyShare *KeyShareCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _KeyShare.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_KeyShare *KeyShareTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_KeyShare *KeyShareTransactorRaw) TransactWithResult(opts *bind.TransactOpts, result interface{}, method string, params ...interface{}) (*types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.contract.TransactWithResult(opts, result, method, params...) +} + +// Desc is a free data retrieval call binding the contract method 0x55f150f1. +// +// Solidity: function desc() constant returns(string, string[]) +func (_KeyShare *KeyShareCaller) Desc(opts *bind.CallOpts) (string, []string, error) { + var ( + ret0 = new(string) + ret1 = new([]string) + ) + out := &[]interface{}{ + ret0, + ret1, + } + err := _KeyShare.contract.Call(opts, out, "desc") + return *ret0, *ret1, err +} + +// Desc is a free data retrieval call binding the contract method 0x55f150f1. +// +// Solidity: function desc() constant returns(string, string[]) +func (_KeyShare *KeyShareSession) Desc() (string, []string, error) { + return _KeyShare.Contract.Desc(&_KeyShare.CallOpts) +} + +// Desc is a free data retrieval call binding the contract method 0x55f150f1. +// +// Solidity: function desc() constant returns(string, string[]) +func (_KeyShare *KeyShareCallerSession) Desc() (string, []string, error) { + return _KeyShare.Contract.Desc(&_KeyShare.CallOpts) +} + +// Select is a free data retrieval call binding the contract method 0xfcd7e3c1. +// +// Solidity: function select(string id) constant returns(string, string) +func (_KeyShare *KeyShareCaller) Select(opts *bind.CallOpts, id string) (string, string, error) { + var ( + ret0 = new(string) + ret1 = new(string) + ) + out := &[]interface{}{ + ret0, + ret1, + } + err := _KeyShare.contract.Call(opts, out, "select", id) + return *ret0, *ret1, err +} + +// Select is a free data retrieval call binding the contract method 0xfcd7e3c1. +// +// Solidity: function select(string id) constant returns(string, string) +func (_KeyShare *KeyShareSession) Select(id string) (string, string, error) { + return _KeyShare.Contract.Select(&_KeyShare.CallOpts, id) +} + +// Select is a free data retrieval call binding the contract method 0xfcd7e3c1. +// +// Solidity: function select(string id) constant returns(string, string) +func (_KeyShare *KeyShareCallerSession) Select(id string) (string, string, error) { + return _KeyShare.Contract.Select(&_KeyShare.CallOpts, id) +} + +// CreateTable is a paid mutator transaction binding the contract method 0x6a5bae4e. +// +// Solidity: function createTable(string tableName, string key, string[] fields) returns(int256) +func (_KeyShare *KeyShareTransactor) CreateTable(opts *bind.TransactOpts, tableName string, key string, fields []string) (*big.Int, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "createTable", tableName, key, fields) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncCreateTable(handler func(*types.Receipt, error), opts *bind.TransactOpts, tableName string, key string, fields []string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "createTable", tableName, key, fields) +} + +// CreateTable is a paid mutator transaction binding the contract method 0x6a5bae4e. +// +// Solidity: function createTable(string tableName, string key, string[] fields) returns(int256) +func (_KeyShare *KeyShareSession) CreateTable(tableName string, key string, fields []string) (*big.Int, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.CreateTable(&_KeyShare.TransactOpts, tableName, key, fields) +} + +func (_KeyShare *KeyShareSession) AsyncCreateTable(handler func(*types.Receipt, error), tableName string, key string, fields []string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncCreateTable(handler, &_KeyShare.TransactOpts, tableName, key, fields) +} + +// CreateTable is a paid mutator transaction binding the contract method 0x6a5bae4e. +// +// Solidity: function createTable(string tableName, string key, string[] fields) returns(int256) +func (_KeyShare *KeyShareTransactorSession) CreateTable(tableName string, key string, fields []string) (*big.Int, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.CreateTable(&_KeyShare.TransactOpts, tableName, key, fields) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncCreateTable(handler func(*types.Receipt, error), tableName string, key string, fields []string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncCreateTable(handler, &_KeyShare.TransactOpts, tableName, key, fields) +} + +// Insert is a paid mutator transaction binding the contract method 0x2fe99bdc. +// +// Solidity: function insert(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactor) Insert(opts *bind.TransactOpts, id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(int32) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "insert", id, name, secret) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncInsert(handler func(*types.Receipt, error), opts *bind.TransactOpts, id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "insert", id, name, secret) +} + +// Insert is a paid mutator transaction binding the contract method 0x2fe99bdc. +// +// Solidity: function insert(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareSession) Insert(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Insert(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareSession) AsyncInsert(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncInsert(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// Insert is a paid mutator transaction binding the contract method 0x2fe99bdc. +// +// Solidity: function insert(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactorSession) Insert(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Insert(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncInsert(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncInsert(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// Remove is a paid mutator transaction binding the contract method 0x80599e4b. +// +// Solidity: function remove(string id) returns(int32) +func (_KeyShare *KeyShareTransactor) Remove(opts *bind.TransactOpts, id string) (int32, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(int32) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "remove", id) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncRemove(handler func(*types.Receipt, error), opts *bind.TransactOpts, id string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "remove", id) +} + +// Remove is a paid mutator transaction binding the contract method 0x80599e4b. +// +// Solidity: function remove(string id) returns(int32) +func (_KeyShare *KeyShareSession) Remove(id string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Remove(&_KeyShare.TransactOpts, id) +} + +func (_KeyShare *KeyShareSession) AsyncRemove(handler func(*types.Receipt, error), id string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncRemove(handler, &_KeyShare.TransactOpts, id) +} + +// Remove is a paid mutator transaction binding the contract method 0x80599e4b. +// +// Solidity: function remove(string id) returns(int32) +func (_KeyShare *KeyShareTransactorSession) Remove(id string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Remove(&_KeyShare.TransactOpts, id) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncRemove(handler func(*types.Receipt, error), id string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncRemove(handler, &_KeyShare.TransactOpts, id) +} + +// Update is a paid mutator transaction binding the contract method 0x31c3e456. +// +// Solidity: function update(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactor) Update(opts *bind.TransactOpts, id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + var ( + ret0 = new(int32) + ) + out := ret0 + transaction, receipt, err := _KeyShare.contract.TransactWithResult(opts, out, "update", id, name, secret) + return *ret0, transaction, receipt, err +} + +func (_KeyShare *KeyShareTransactor) AsyncUpdate(handler func(*types.Receipt, error), opts *bind.TransactOpts, id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.contract.AsyncTransact(opts, handler, "update", id, name, secret) +} + +// Update is a paid mutator transaction binding the contract method 0x31c3e456. +// +// Solidity: function update(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareSession) Update(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Update(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareSession) AsyncUpdate(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncUpdate(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// Update is a paid mutator transaction binding the contract method 0x31c3e456. +// +// Solidity: function update(string id, string name, string secret) returns(int32) +func (_KeyShare *KeyShareTransactorSession) Update(id string, name string, secret string) (int32, *types.Transaction, *types.Receipt, error) { + return _KeyShare.Contract.Update(&_KeyShare.TransactOpts, id, name, secret) +} + +func (_KeyShare *KeyShareTransactorSession) AsyncUpdate(handler func(*types.Receipt, error), id string, name string, secret string) (*types.Transaction, error) { + return _KeyShare.Contract.AsyncUpdate(handler, &_KeyShare.TransactOpts, id, name, secret) +} + +// KeyShareCreateResult represents a CreateResult event raised by the KeyShare contract. +type KeyShareCreateResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchCreateResult is a free log subscription operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "CreateResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "CreateResult") +} + +// ParseCreateResult is a log parse operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseCreateResult(log types.Log) (*KeyShareCreateResult, error) { + event := new(KeyShareCreateResult) + if err := _KeyShare.contract.UnpackLog(event, "CreateResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchCreateResult is a free log subscription operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareSession) WatchCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchCreateResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllCreateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllCreateResult(fromBlock, handler) +} + +// ParseCreateResult is a log parse operation binding the contract event 0xb5636cd912a73dcdb5b570dbe331dfa3e6435c93e029e642def2c8e40dacf210. +// +// Solidity: event CreateResult(int256 count) +func (_KeyShare *KeyShareSession) ParseCreateResult(log types.Log) (*KeyShareCreateResult, error) { + return _KeyShare.Contract.ParseCreateResult(log) +} + +// KeyShareInsertResult represents a InsertResult event raised by the KeyShare contract. +type KeyShareInsertResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchInsertResult is a free log subscription operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "InsertResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "InsertResult") +} + +// ParseInsertResult is a log parse operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseInsertResult(log types.Log) (*KeyShareInsertResult, error) { + event := new(KeyShareInsertResult) + if err := _KeyShare.contract.UnpackLog(event, "InsertResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchInsertResult is a free log subscription operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareSession) WatchInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchInsertResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllInsertResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllInsertResult(fromBlock, handler) +} + +// ParseInsertResult is a log parse operation binding the contract event 0xc57b01fa77f41df77eaab79a0e2623fab2e7ae3e9530d9b1cab225ad65f2b7ce. +// +// Solidity: event InsertResult(int256 count) +func (_KeyShare *KeyShareSession) ParseInsertResult(log types.Log) (*KeyShareInsertResult, error) { + return _KeyShare.Contract.ParseInsertResult(log) +} + +// KeyShareRemoveResult represents a RemoveResult event raised by the KeyShare contract. +type KeyShareRemoveResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchRemoveResult is a free log subscription operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "RemoveResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "RemoveResult") +} + +// ParseRemoveResult is a log parse operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseRemoveResult(log types.Log) (*KeyShareRemoveResult, error) { + event := new(KeyShareRemoveResult) + if err := _KeyShare.contract.UnpackLog(event, "RemoveResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchRemoveResult is a free log subscription operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareSession) WatchRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchRemoveResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllRemoveResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllRemoveResult(fromBlock, handler) +} + +// ParseRemoveResult is a log parse operation binding the contract event 0x4b930e280fe29620bdff00c88155d46d6d82a39f45dd5c3ea114dc3157358112. +// +// Solidity: event RemoveResult(int256 count) +func (_KeyShare *KeyShareSession) ParseRemoveResult(log types.Log) (*KeyShareRemoveResult, error) { + return _KeyShare.Contract.ParseRemoveResult(log) +} + +// KeyShareUpdateResult represents a UpdateResult event raised by the KeyShare contract. +type KeyShareUpdateResult struct { + Count *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// WatchUpdateResult is a free log subscription operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareFilterer) WatchUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "UpdateResult") +} + +func (_KeyShare *KeyShareFilterer) WatchAllUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.contract.WatchLogs(fromBlock, handler, "UpdateResult") +} + +// ParseUpdateResult is a log parse operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareFilterer) ParseUpdateResult(log types.Log) (*KeyShareUpdateResult, error) { + event := new(KeyShareUpdateResult) + if err := _KeyShare.contract.UnpackLog(event, "UpdateResult", log); err != nil { + return nil, err + } + return event, nil +} + +// WatchUpdateResult is a free log subscription operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareSession) WatchUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchUpdateResult(fromBlock, handler) +} + +func (_KeyShare *KeyShareSession) WatchAllUpdateResult(fromBlock *int64, handler func(int, []types.Log)) (string, error) { + return _KeyShare.Contract.WatchAllUpdateResult(fromBlock, handler) +} + +// ParseUpdateResult is a log parse operation binding the contract event 0x8e5890af40fc24a059396aca2f83d6ce41fcef086876548fa4fb8ec27e9d292a. +// +// Solidity: event UpdateResult(int256 count) +func (_KeyShare *KeyShareSession) ParseUpdateResult(log types.Log) (*KeyShareUpdateResult, error) { + return _KeyShare.Contract.ParseUpdateResult(log) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.sol new file mode 100644 index 000000000..d30783026 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/KeyShare.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +import "./Table.sol"; + +contract KeyShare { + event CreateResult(int256 count); + event InsertResult(int256 count); + event UpdateResult(int256 count); + event RemoveResult(int256 count); + + TableManager constant tm = TableManager(address(0x1002)); + Table table; + string constant TABLE_NAME = "t_key_share"; + constructor () public{ + // create table + string[] memory columnNames = new string[](2); + columnNames[0] = "name"; + columnNames[1] = "secret"; + TableInfo memory tf = TableInfo("id", columnNames); + + tm.createTable(TABLE_NAME, tf); + address t_address = tm.openTable(TABLE_NAME); + require(t_address!=address(0x0),""); + table = Table(t_address); + } + + function select(string memory id) public view returns (string memory,string memory) + { + Entry memory entry = table.select(id); + + string memory name; + string memory secret; + if(entry.fields.length==2){ + name = entry.fields[0]; + secret = entry.fields[1]; + } + return (name,secret); + } + + function insert(string memory id,string memory name,string memory secret) public returns (int32){ + string[] memory columns = new string[](2); + columns[0] = name; + columns[1] = secret; + Entry memory entry = Entry(id, columns); + int32 result = table.insert(entry); + emit InsertResult(result); + return result; + } + + function update(string memory id, string memory name, string memory secret) public returns (int32){ + UpdateField[] memory updateFields = new UpdateField[](2); + updateFields[0] = UpdateField("name", name); + updateFields[1] = UpdateField("secret", secret); + + int32 result = table.update(id, updateFields); + emit UpdateResult(result); + return result; + } + + function remove(string memory id) public returns(int32){ + int32 result = table.remove(id); + emit RemoveResult(result); + return result; + } + + function createTable(string memory tableName,string memory key,string[] memory fields) public returns(int256){ + TableInfo memory tf = TableInfo(key, fields); + int32 result = tm.createTable(tableName,tf); + emit CreateResult(result); + return result; + } + + function desc() public view returns(string memory, string[] memory){ + TableInfo memory ti = tm.desc(TABLE_NAME); + return (ti.keyColumn,ti.valueColumns); + } +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.abi new file mode 100644 index 000000000..900206549 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.abi @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"}],"name":"count","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"internalType":"struct Entry","name":"entry","type":"tuple"}],"name":"insert","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"},{"components":[{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint32","name":"count","type":"uint32"}],"internalType":"struct Limit","name":"limit","type":"tuple"}],"name":"remove","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"remove","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"},{"components":[{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint32","name":"count","type":"uint32"}],"internalType":"struct Limit","name":"limit","type":"tuple"}],"name":"select","outputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"internalType":"struct Entry[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"select","outputs":[{"components":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string[]","name":"fields","type":"string[]"}],"internalType":"struct Entry","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"components":[{"internalType":"string","name":"columnName","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct UpdateField[]","name":"updateFields","type":"tuple[]"}],"name":"update","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConditionOP","name":"op","type":"uint8"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct Condition[]","name":"conditions","type":"tuple[]"},{"components":[{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint32","name":"count","type":"uint32"}],"internalType":"struct Limit","name":"limit","type":"tuple"},{"components":[{"internalType":"string","name":"columnName","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct UpdateField[]","name":"updateFields","type":"tuple[]"}],"name":"update","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.bin new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.sol new file mode 100644 index 000000000..65169a9e6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/Table.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// 该接口文件定义了FISCO BCOS v3.1.0及以前版本的接口,使用时需要将该文件放在合约目录下 +// 若要使用FISCO BCOS v3.2.0及以后版本的接口,请使用TableV320.sol,旧合约仍然能在新节点中使用 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字 +// enum KeyOrder {Lexicographic, Numerical} + struct TableInfo { + string keyColumn; + string[] valueColumns; + } + +// 记录,用于select和insert + struct Entry { + string key; + string[] fields; // 考虑2.0的Entry接口,临时Precompiled的问题,考虑加工具类接口 + } + +// 更新字段,用于update + struct UpdateField { + string columnName; + // 考虑工具类 + string value; + } + +// 筛选条件,大于、大于等于、小于、小于等于 + enum ConditionOP {GT, GE, LT, LE} + struct Condition { + ConditionOP op; + // string field; + string value; + } + +// 数量限制 + struct Limit { + uint32 offset; + // count limit max is 500 + uint32 count; + } + +// 表管理合约,是静态Precompiled,有固定的合约地址 +abstract contract TableManager { + // 创建表,传入TableInfo + function createTable(string memory path, TableInfo memory tableInfo) public virtual returns (int32); + + // 创建KV表,传入key和value字段名 + function createKVTable(string memory tableName, string memory keyField, string memory valueField) public virtual returns (int32); + + // 只提供给Solidity合约调用时使用 + function openTable(string memory path) public view virtual returns (address); + + // 变更表字段 + // 只能新增字段,不能删除字段,新增的字段默认值为空,不能与原有字段重复 + function appendColumns(string memory path, string[] memory newColumns) public virtual returns (int32); + + // 获取表信息 + function desc(string memory tableName) public view virtual returns (TableInfo memory); +} + +// 表合约,是动态Precompiled,TableManager创建时指定地址 +abstract contract Table { + // 按key查询entry + function select(string memory key) public virtual view returns (Entry memory); + + // 按条件批量查询entry,condition为空则查询所有记录 + function select(Condition[] memory conditions, Limit memory limit) public virtual view returns (Entry[] memory); + + // 按照条件查询count数据 + function count(Condition[] memory conditions) public virtual view returns (uint32); + + // 插入数据 + function insert(Entry memory entry) public virtual returns (int32); + + // 按key更新entry + function update(string memory key, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按条件批量更新entry,condition为空则更新所有记录 + function update(Condition[] memory conditions, Limit memory limit, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按key删除entry + function remove(string memory key) public virtual returns (int32); + // 按条件批量删除entry,condition为空则删除所有记录 + function remove(Condition[] memory conditions, Limit memory limit) public virtual returns (int32); +} + +abstract contract KVTable { + function get(string memory key) public view virtual returns (bool, string memory); + + function set(string memory key, string memory value) public virtual returns (int32); +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/TableManager.abi b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/TableManager.abi new file mode 100644 index 000000000..52e130933 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/TableManager.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"path","type":"string"},{"internalType":"string[]","name":"newColumns","type":"string[]"}],"name":"appendColumns","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tableName","type":"string"},{"internalType":"string","name":"keyField","type":"string"},{"internalType":"string","name":"valueField","type":"string"}],"name":"createKVTable","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"path","type":"string"},{"components":[{"internalType":"string","name":"keyColumn","type":"string"},{"internalType":"string[]","name":"valueColumns","type":"string[]"}],"internalType":"struct TableInfo","name":"tableInfo","type":"tuple"}],"name":"createTable","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tableName","type":"string"}],"name":"desc","outputs":[{"components":[{"internalType":"string","name":"keyColumn","type":"string"},{"internalType":"string[]","name":"valueColumns","type":"string[]"}],"internalType":"struct TableInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"path","type":"string"}],"name":"openTable","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/TableManager.bin b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/KeyShare/TableManager.bin new file mode 100644 index 000000000..e69de29bb diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/README.md new file mode 100644 index 000000000..36e587321 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/README.md @@ -0,0 +1,26 @@ +# DecisionStorage + +合约名 `DecisionStorage`,用于在区块链上存储和管理决策记录 + +- **Decision**: 用于表示单个决策记录,包含以下字段: + - `evidence`: 证据的字符串表示。 + - `nodeID`: 节点的唯一标识符。 + - `timestamp`: 决策记录的时间戳。 + - `approved`: 表示决策是否被批准的布尔值。 + +# KeyShare + +`KeyShare`,用于在区块链上管理密钥共享信息。使用 `Table` 外部合约来实现数据存储和管理。以下是合约的主要功能和结构: + +### 事件 + +- **CreateResult**: 创建表的结果。 +- **InsertResult**: 插入记录的结果。 +- **UpdateResult**: 更新记录的结果。 +- **RemoveResult**: 移除记录的结果。 + +### 状态变量 + +- **tm**: `TableManager` 合约的地址,用于管理表。 +- **table**: 当前操作的表。 +- **TABLE_NAME**: 表的名称,固定为 `"t_key_share"`。 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/DecisionStorage.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/DecisionStorage.sol new file mode 100644 index 000000000..19c90c817 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/DecisionStorage.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.10 <0.8.20; + +contract DecisionStorage { + // 决策记录结构 + struct Decision { + string evidence; // 证据 + string nodeID; // 节点ID + uint256 timestamp; // 时间戳 + bool approved; // 是否批准 + } + + // 存储所有决策记录 + Decision[] public decisions; + + // 记录决策的事件 + event DecisionRecorded( + string evidence, + string nodeID, + uint256 timestamp, + bool approved + ); + + function recordDecision(string memory evidence, string memory nodeID, bool approved) public returns (bool) { + Decision memory newDecision = Decision({ + evidence: evidence, + nodeID: nodeID, + timestamp: block.timestamp, + approved: approved + }); + + // 存储决策 + decisions.push(newDecision); + + // 触发事件 + emit DecisionRecorded( + newDecision.evidence, + newDecision.nodeID, + newDecision.timestamp, + newDecision.approved + ); + + return true; + } + + // 验证证据 可以添加具体的验证逻辑 + function verifyEvidence(string memory evidence) public view returns (bool) { + // 目前返回true用于测试 + return true; + } + + // 获取决策记录数量 + function getDecisionCount() public view returns (uint256) { + return decisions.length; + } + + // 获取指定索引的决策记录 + function getDecision(uint256 index) public view returns ( + string memory evidence, + string memory nodeID, + uint256 timestamp, + bool approved + ) { + require(index < decisions.length, "Index out of bounds"); + Decision memory decision = decisions[index]; + return ( + decision.evidence, + decision.nodeID, + decision.timestamp, + decision.approved + ); + } + +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/KeyShare.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/KeyShare.sol new file mode 100644 index 000000000..d30783026 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/KeyShare.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +import "./Table.sol"; + +contract KeyShare { + event CreateResult(int256 count); + event InsertResult(int256 count); + event UpdateResult(int256 count); + event RemoveResult(int256 count); + + TableManager constant tm = TableManager(address(0x1002)); + Table table; + string constant TABLE_NAME = "t_key_share"; + constructor () public{ + // create table + string[] memory columnNames = new string[](2); + columnNames[0] = "name"; + columnNames[1] = "secret"; + TableInfo memory tf = TableInfo("id", columnNames); + + tm.createTable(TABLE_NAME, tf); + address t_address = tm.openTable(TABLE_NAME); + require(t_address!=address(0x0),""); + table = Table(t_address); + } + + function select(string memory id) public view returns (string memory,string memory) + { + Entry memory entry = table.select(id); + + string memory name; + string memory secret; + if(entry.fields.length==2){ + name = entry.fields[0]; + secret = entry.fields[1]; + } + return (name,secret); + } + + function insert(string memory id,string memory name,string memory secret) public returns (int32){ + string[] memory columns = new string[](2); + columns[0] = name; + columns[1] = secret; + Entry memory entry = Entry(id, columns); + int32 result = table.insert(entry); + emit InsertResult(result); + return result; + } + + function update(string memory id, string memory name, string memory secret) public returns (int32){ + UpdateField[] memory updateFields = new UpdateField[](2); + updateFields[0] = UpdateField("name", name); + updateFields[1] = UpdateField("secret", secret); + + int32 result = table.update(id, updateFields); + emit UpdateResult(result); + return result; + } + + function remove(string memory id) public returns(int32){ + int32 result = table.remove(id); + emit RemoveResult(result); + return result; + } + + function createTable(string memory tableName,string memory key,string[] memory fields) public returns(int256){ + TableInfo memory tf = TableInfo(key, fields); + int32 result = tm.createTable(tableName,tf); + emit CreateResult(result); + return result; + } + + function desc() public view returns(string memory, string[] memory){ + TableInfo memory ti = tm.desc(TABLE_NAME); + return (ti.keyColumn,ti.valueColumns); + } +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/Table.sol b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/Table.sol new file mode 100644 index 000000000..65169a9e6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/contract/source/Table.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// 该接口文件定义了FISCO BCOS v3.1.0及以前版本的接口,使用时需要将该文件放在合约目录下 +// 若要使用FISCO BCOS v3.2.0及以后版本的接口,请使用TableV320.sol,旧合约仍然能在新节点中使用 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字 +// enum KeyOrder {Lexicographic, Numerical} + struct TableInfo { + string keyColumn; + string[] valueColumns; + } + +// 记录,用于select和insert + struct Entry { + string key; + string[] fields; // 考虑2.0的Entry接口,临时Precompiled的问题,考虑加工具类接口 + } + +// 更新字段,用于update + struct UpdateField { + string columnName; + // 考虑工具类 + string value; + } + +// 筛选条件,大于、大于等于、小于、小于等于 + enum ConditionOP {GT, GE, LT, LE} + struct Condition { + ConditionOP op; + // string field; + string value; + } + +// 数量限制 + struct Limit { + uint32 offset; + // count limit max is 500 + uint32 count; + } + +// 表管理合约,是静态Precompiled,有固定的合约地址 +abstract contract TableManager { + // 创建表,传入TableInfo + function createTable(string memory path, TableInfo memory tableInfo) public virtual returns (int32); + + // 创建KV表,传入key和value字段名 + function createKVTable(string memory tableName, string memory keyField, string memory valueField) public virtual returns (int32); + + // 只提供给Solidity合约调用时使用 + function openTable(string memory path) public view virtual returns (address); + + // 变更表字段 + // 只能新增字段,不能删除字段,新增的字段默认值为空,不能与原有字段重复 + function appendColumns(string memory path, string[] memory newColumns) public virtual returns (int32); + + // 获取表信息 + function desc(string memory tableName) public view virtual returns (TableInfo memory); +} + +// 表合约,是动态Precompiled,TableManager创建时指定地址 +abstract contract Table { + // 按key查询entry + function select(string memory key) public virtual view returns (Entry memory); + + // 按条件批量查询entry,condition为空则查询所有记录 + function select(Condition[] memory conditions, Limit memory limit) public virtual view returns (Entry[] memory); + + // 按照条件查询count数据 + function count(Condition[] memory conditions) public virtual view returns (uint32); + + // 插入数据 + function insert(Entry memory entry) public virtual returns (int32); + + // 按key更新entry + function update(string memory key, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按条件批量更新entry,condition为空则更新所有记录 + function update(Condition[] memory conditions, Limit memory limit, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按key删除entry + function remove(string memory key) public virtual returns (int32); + // 按条件批量删除entry,condition为空则删除所有记录 + function remove(Condition[] memory conditions, Limit memory limit) public virtual returns (int32); +} + +abstract contract KVTable { + function get(string memory key) public view virtual returns (bool, string memory); + + function set(string memory key, string memory value) public virtual returns (int32); +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/pool.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/pool.go new file mode 100644 index 000000000..b9b5af60e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/pool.go @@ -0,0 +1,97 @@ +package controller + +import ( + "errors" + "fmt" + "hufu/model" + "log" + "math/rand" + "sync" +) + +// WalletPool 是一个管理钱包的全局池 +type WalletPool struct { + wallets []*model.Wallet + mutex sync.RWMutex +} + +// 全局 WalletPool 实例 +var GlobalWalletPool *WalletPool + +const ( + initCount = 10 + initBalance = 1000000 + initPrefix = "ProxyWallet" +) + +// InitWalletPool 初始化全局 WalletPool +func InitWalletPool() { + GlobalWalletPool = &WalletPool{ + wallets: make([]*model.Wallet, 0), + } + + // 获取所有以 ProxyWallet 开头的钱包 + var existingWallets []*model.Wallet + if err := model.DB.Where("wallet_name LIKE ?", fmt.Sprintf("%s%%", initPrefix)).Find(&existingWallets).Error; err != nil { + log.Fatal(err) + } + + if len(existingWallets) == 0 { + // 如果池中没有钱包,则创建新的钱包 + for i := 0; i < initCount; i++ { + wallet, err := NewWallet(fmt.Sprintf("%s%d", initPrefix, i), fmt.Sprintf("proxy-user%d", i), initBalance) + if err != nil { + log.Fatal(err) + } + GlobalWalletPool.AddWallet(wallet) + } + } else { + GlobalWalletPool.wallets = existingWallets + } +} + +// AddWallet 向池中添加钱包 +func (wp *WalletPool) AddWallet(wallet *model.Wallet) { + wp.mutex.Lock() + defer wp.mutex.Unlock() + wp.wallets = append(wp.wallets, wallet) +} + +// GetRandomWallet 从池中随机获取一个钱包 +func (wp *WalletPool) GetRandomWallet() *model.Wallet { + wp.mutex.RLock() + defer wp.mutex.RUnlock() + if len(wp.wallets) == 0 { + return nil + } + return wp.wallets[rand.Intn(len(wp.wallets))] +} + +// GetAllWallets 获取池中所有钱包 +func (wp *WalletPool) GetAllWallets() []*model.Wallet { + wp.mutex.RLock() + defer wp.mutex.RUnlock() + return wp.wallets +} + +// GetRandomWallets 从池中随机获取指定数量的钱包 +func (wp *WalletPool) GetRandomWallets(count int) ([]*model.Wallet, error) { + wp.mutex.RLock() + defer wp.mutex.RUnlock() + + if len(wp.wallets) == 0 { + return nil, errors.New("no wallets in the pool") + } + + if count >= len(wp.wallets) { + return nil, errors.New("not enough wallets in the pool") + } + + selectedWallets := make([]*model.Wallet, count) + indexes := rand.Perm(len(wp.wallets)) + for i := 0; i < count; i++ { + selectedWallets[i] = wp.wallets[indexes[i]] + } + + return selectedWallets, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/regulator.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/regulator.go new file mode 100644 index 000000000..1c4eed2ec --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/regulator.go @@ -0,0 +1,132 @@ +package controller + +import ( + "fmt" + "time" + + "hufu/model" + "hufu/supervisor" + "hufu/utils" +) + +type Regulator struct { + maxTransactionAmount float64 + maxDailyAmount float64 + suspiciousFrequency int // 每小时可疑交易频率阈值 +} + +func NewRegulator() *Regulator { + return &Regulator{ + maxTransactionAmount: 10000, + maxDailyAmount: 50000, + suspiciousFrequency: 100, + } +} + +func (r *Regulator) SendAlert(tx *model.Transaction, evidence string) error { + return nil +} + +func (r *Regulator) CheckTransaction(tx *model.Transaction, w *model.Wallet) error { + // 检查最大交易金额 + if tx.Amount > r.maxTransactionAmount { + return fmt.Errorf("交易金额超过允许的最大值: %f", r.maxTransactionAmount) + } + + // 检查日累计交易金额 + dailyAmount, _ := r.getDailyTransactionAmount(w.ID) + if dailyAmount+tx.Amount > r.maxDailyAmount { + return fmt.Errorf("日交易总额超过限制: %f", r.maxDailyAmount) + } + + // 检查交易频率 + frequency, err := r.getHourlyTransactionFrequency(w.ID) + if err != nil { + return fmt.Errorf("获取交易频率失败: %v", err) + } + if frequency >= r.suspiciousFrequency { + // 记录可疑交易 + return fmt.Errorf("交易频率过高") + } + + return nil +} + +// 获取钱包当日交易总额 +func (r *Regulator) getDailyTransactionAmount(walletID uint) (float64, error) { + var totalAmount float64 + today := time.Now().Format("2006-01-02") + + err := model.DB.Model(&model.Transaction{}). + Where("wallet_id = ? AND DATE(created_at) = ?", walletID, today). + Select("COALESCE(SUM(amount), 0)"). + Row(). + Scan(&totalAmount) + + return totalAmount, err +} + +// 获取每小时交易频率 +func (r *Regulator) getHourlyTransactionFrequency(walletID uint) (int, error) { + var count int64 + oneHourAgo := time.Now().Add(-time.Hour) + + err := model.DB.Model(&model.Transaction{}). + Where("from_wallet_id = ? AND created_at >= ?", walletID, oneHourAgo). + Count(&count).Error + + return int(count), err +} + +func ProcessPrivateKey(walletID uint, evidence string) ([]string, error) { + // 获取私钥 + walletKey, err := GetWalletKeyByWalletID(walletID) + if err != nil { + return nil, fmt.Errorf("failed to get wallet key: %v", err) + } + + parts, err := utils.SharePrivateKey(walletKey.PrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to share private key: %v", err) + } + + approved, err := supervisor.JuryInstance.HandleRegulatoryRequest(fmt.Sprintf("wallet-%d-%s", walletID, evidence)) + if err != nil { + return nil, fmt.Errorf("failed to handle regulatory request: %v", err) + } + + res := []string{} + + for i, part := range parts { + if approved[supervisor.JuryInstance.Nodes[i].NodeID] { + encryptedPart, err := utils.EncryptData(supervisor.JuryInstance.Nodes[i].PublicKey, part) + if err != nil { + return nil, fmt.Errorf("failed to encrypt part %d: %v", i, err) + } + // 存储加密后的分片 + id := fmt.Sprintf("%d-%d", walletID, i) + name := fmt.Sprintf("key_share_%s", id) + err = supervisor.JuryInstance.Nodes[i].StoreEncryptedKeyShare(id, name, encryptedPart) + if err != nil { + return nil, fmt.Errorf("failed to store encrypted part %d: %v", i, err) + } + + res = append(res, part) + } + } + + return res, nil +} + +// GetAbnormalTransactions 获取所有异常交易 +func GetAbnormalTransactions() ([]model.AbnormalTransaction, error) { + var transactions []model.AbnormalTransaction + + // 从数据库中查询标记为异常的交易 + result := model.DB.Find(&transactions) + if result.Error != nil { + return nil, result.Error + } + + return transactions, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/tee_controller.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/tee_controller.go new file mode 100644 index 000000000..57e2ba0fa --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/tee_controller.go @@ -0,0 +1,269 @@ +package controller + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "log" + "math/big" + "net/http" + "strconv" + "strings" + + "golang.org/x/net/proxy" +) + +type TeeController struct{} + +func NewTeeController() *TeeController { + return &TeeController{} +} + +// 创建代理客户端的通用函数 +func (tc *TeeController) createProxyClient() (*http.Client, error) { + dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:1080", nil, proxy.Direct) + if err != nil { + return nil, err + } + + return &http.Client{ + Transport: &http.Transport{ + Dial: dialer.Dial, + }, + }, nil +} + +// 发送HTTP请求的通用函数 +func (tc *TeeController) sendRequest(client *http.Client, method, url string, body io.Reader, contentType string) ([]byte, int, error) { + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, http.StatusInternalServerError, err + } + + req.Header.Set("Content-Type", contentType) + + resp, err := client.Do(req) + if err != nil { + return nil, http.StatusInternalServerError, err + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, http.StatusInternalServerError, err + } + + return respBody, resp.StatusCode, nil +} + +func (tc *TeeController) Add(add string) ([]byte, int, error) { + client, err := tc.createProxyClient() + if err != nil { + return nil, http.StatusInternalServerError, err + } + + return tc.sendRequest( + client, + "POST", + "http://10.77.110.184:8080/add", + strings.NewReader(add), + "text/plain", + ) +} + +func (tc *TeeController) GenerateKey(walletID int) ([]byte, int, error) { + client, err := tc.createProxyClient() + if err != nil { + return nil, http.StatusInternalServerError, err + } + + request := struct { + WalletID int `json:"wallet_id"` + }{ + WalletID: walletID, + } + + jsonBody, _ := json.Marshal(request) + return tc.sendRequest( + client, + "POST", + "http://10.77.110.184:8082/api/create_wallet", + bytes.NewBuffer(jsonBody), + "application/json", + ) +} + +func (tc *TeeController) Warning(from, to int, amount float64) ([]byte, int, error) { + client, err := tc.createProxyClient() + if err != nil { + return nil, http.StatusInternalServerError, err + } + + request := struct { + From int `json:"from"` + To int `json:"to"` + Amount float64 `json:"amount"` + }{ + From: from, + To: to, + Amount: amount, + } + + jsonBody, _ := json.Marshal(request) + return tc.sendRequest( + client, + "POST", + "http://10.77.110.184:8082/api/transaction_warning", + bytes.NewBuffer(jsonBody), + "application/json", + ) +} + +func (tc *TeeController) Shuffle(from, to int, amount float64) ([]byte, int, error) { + client, err := tc.createProxyClient() + if err != nil { + return nil, http.StatusInternalServerError, err + } + + request := struct { + From int `json:"from"` + To int `json:"to"` + Amount float64 `json:"amount"` + }{ + From: from, + To: to, + Amount: amount, + } + + jsonBody, _ := json.Marshal(request) + return tc.sendRequest( + client, + "POST", + "http://10.77.110.184:8082/api/shuffle_transaction", + bytes.NewBuffer(jsonBody), + "application/json", + ) +} + +func (tc *TeeController) Decrypt(from, to, amount string) ([]byte, int, error) { + client, err := tc.createProxyClient() + if err != nil { + return nil, http.StatusInternalServerError, err + } + + request := struct { + From string `json:"from"` + To string `json:"to"` + Amount string `json:"amount"` + }{ + From: from, + To: to, + Amount: amount, + } + + jsonBody, _ := json.Marshal(request) + return tc.sendRequest( + client, + "POST", + "http://10.77.110.184:8082/api/decrypt_transaction", + bytes.NewBuffer(jsonBody), + "application/json", + ) +} + +func (tc *TeeController) Encrypt(from, to string, amount string) (map[string]string, error) { + fromInt, err := strconv.Atoi(from) + if err != nil { + return nil, fmt.Errorf("无效的 from: %v", err) + } + toInt, err := strconv.Atoi(to) + if err != nil { + return nil, fmt.Errorf("无效的 to: %v", err) + } + amountFloat, err := strconv.ParseFloat(amount, 64) + if err != nil { + return nil, fmt.Errorf("无效的 amount: %v", err) + } + + dataLittleEndian := map[string][]byte{ + "from": tc.intToBytesLittleEndian(fromInt), + "to": tc.intToBytesLittleEndian(toInt), + "amount": tc.floatToBytesLittleEndian(amountFloat), + } + + nHex := "922e9ebb9ede6554aadb495da961f38775c226259adf3bd18e00ddc5c099c40d8a714be8f6dbdcac72e1adb21675966a2f541c8f50a0a1ea3cf3d429134b699ddce0b675ee87ac1bc4adae441b0f00ae4fddd0e7b59b058a52357e37a65963f943f2706c678900e7bbb68d6b3eade4ad8ee2c73f3da5244e543f6a6082f2eb31a8c13200945c56980829d0ab9f00261655fc86fd25269f6748dc85b6d7debb03d00c392e04aafef09b41b87d0f1737f66a739d83717d7ef0c3040e10b9202c06b63bd5733626035f657de8dc3e581e237d5b3bbdaa46bde47a06883cf37166b328958217dbccc20c3807099b6a6c8b2dbdf94281a8ff0d29a4053a85572906ede8e141ae3ced4719e8f7fefcd9057435b1aa0e5e84e7a31cf126a84e00d8be983c3939704011174a1f6c2a6ad0808a17eadbc312570a69ee662631a0441bd5eaa96bb9693587cb351015ba1eed3cecf872243ecd822764128ebb338e03a883df35912e75aafb39052d2ecddc2df1270561f7624f7b7aef3dae05a64a7385e99d" + e := 65537 + + n, err := tc.hexToBytes(nHex) + if err != nil { + return nil, fmt.Errorf("Error converting hex to bytes: %v", err) + } + + publicKey := &rsa.PublicKey{ + N: new(big.Int).SetBytes(n), + E: e, + } + + encryptedData := make(map[string]string) + for key, value := range dataLittleEndian { + encrypted, err := tc.rsaEncrypt(publicKey, value) + if err != nil { + return nil, fmt.Errorf("Error encrypting data: %v", err) + } + encryptedData[key] = encrypted + } + + return encryptedData, nil +} + +// 辅助方法 +func (tc *TeeController) hexToBytes(hexStr string) ([]byte, error) { + return hex.DecodeString(hexStr) +} + +func (tc *TeeController) rsaEncrypt(publicKey *rsa.PublicKey, plaintext []byte) (string, error) { + cipherText, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, plaintext, nil) + if err != nil { + return "", err + } + return hex.EncodeToString(cipherText), nil +} + +func (tc *TeeController) intToBytesLittleEndian(n int) []byte { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, int32(n)) + if err != nil { + log.Fatal(err) + } + return buf.Bytes() +} + +func (tc *TeeController) floatToBytesLittleEndian(f float64) []byte { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, f) + if err != nil { + log.Fatal(err) + } + return buf.Bytes() +} + +func (tc *TeeController) GetEncryptedTransaction(id string) ([]byte, int, error) { + client, err := tc.createProxyClient() + if err != nil { + return nil, http.StatusInternalServerError, err + } + + return tc.sendRequest( + client, + "POST", + "http://10.77.110.184:38080/getfile", + bytes.NewBuffer([]byte(fmt.Sprintf(`{"id": "%s"}`, id))), + "application/json", + ) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/transaction.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/transaction.go new file mode 100644 index 000000000..ce3fd987b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/transaction.go @@ -0,0 +1,560 @@ +package controller + +import ( + "fmt" + "hufu/config" + "hufu/errors" + "hufu/model" + "hufu/utils" + "time" + + "gorm.io/gorm" +) + +const ( + TransactionStatusFailed = "failed" + TransactionStatusSuccess = "completed" +) + +const ProxyWalletCount = 3 + +// NormalTransfer 普通转账,不经过代理钱包 +func NormalTransfer(from *model.Wallet, to *model.Wallet, amount float64) (*model.Transaction, error) { + tx := model.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + originalTx, err := createOriginalTransaction(tx, from, to, amount) + if err != nil { + tx.Rollback() + return nil, err + } + + if err := updateWalletBalances(tx, from, to, amount); err != nil { + tx.Rollback() + return nil, err + } + + originalTx.Status = TransactionStatusSuccess + if err := tx.Save(originalTx).Error; err != nil { + tx.Rollback() + return nil, err + } + + return originalTx, tx.Commit().Error +} + +// ProxyTransfer 代理转账, 经过代理钱包 +func ProxyTransfer(from *model.Wallet, to *model.Wallet, amount float64) (*model.Transaction, error) { + tx := model.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 1. 创建并保存原始交易 + originalTx, err := createOriginalTransaction(tx, from, to, amount) + if err != nil { + tx.Rollback() + return nil, err + } + + // 2. 验证交易合规性 + if err := validateTransaction(tx, from, originalTx); err != nil { + // 如果是不合规的交易,直接提交事务并返回 + // 因为validateTransaction已经更新了交易状态并创建了异常记录 + if err := tx.Commit().Error; err != nil { + tx.Rollback() + return nil, err + } + return originalTx, nil + } + + // 3. 创建关联交易记录 + if err := createAssociatedTransactions(tx, originalTx); err != nil { + tx.Rollback() + return nil, err + } + + // 4. 更新钱包余额 + if err := updateWalletBalances(tx, from, to, amount); err != nil { + tx.Rollback() + return nil, err + } + + // 5. 完成交易 + if err := finalizeTransaction(tx, originalTx); err != nil { + tx.Rollback() + return nil, err + } + + return originalTx, tx.Commit().Error +} + +// GetTransferHistory 获取转账历史 +func GetTransferHistory(walletID uint, page, pageSize int) (*model.PageResult, error) { + var total int64 + + // 定义包含用户名的查询结果结构 + type TransactionWithUsername struct { + model.Transaction + FromUsername string `json:"from_username"` + ToUsername string `json:"to_username"` + } + + var transactions []TransactionWithUsername + offset := (page - 1) * pageSize + + // 获取总数 + if err := model.DB.Model(&model.Transaction{}). + Where("(from_wallet_id = ? OR to_wallet_id = ?) AND type = ?", + walletID, walletID, model.DirectTransaction). + Count(&total).Error; err != nil { + return nil, err + } + + // 使用JOIN查询获取用户名 + if err := model.DB.Table("transactions"). + Select("transactions.*, fw.username as from_username, tw.username as to_username"). + Joins("LEFT JOIN wallets fw ON transactions.from_wallet_id = fw.id"). + Joins("LEFT JOIN wallets tw ON transactions.to_wallet_id = tw.id"). + Where("(transactions.from_wallet_id = ? OR transactions.to_wallet_id = ?) AND transactions.type = ?", + walletID, walletID, model.DirectTransaction). + Limit(pageSize). + Offset(offset). + Order("transactions.created_at DESC"). + Find(&transactions).Error; err != nil { + return nil, err + } + + return &model.PageResult{ + List: transactions, + Total: total, + Page: page, + PageSize: pageSize, + }, nil +} + +// createOriginalTransaction 创建原始交易记录 +func createOriginalTransaction(tx *gorm.DB, from *model.Wallet, to *model.Wallet, amount float64) (*model.Transaction, error) { + originalTx := &model.Transaction{ + FromWalletID: from.ID, + ToWalletID: to.ID, + Amount: amount, + Type: model.DirectTransaction, + Status: "pending", + } + return originalTx, tx.Create(originalTx).Error +} + +// validateTransaction 验证交易合规性 +func validateTransaction(tx *gorm.DB, from *model.Wallet, originalTx *model.Transaction) error { + regulator := NewRegulator() + if err := regulator.CheckTransaction(originalTx, from); err != nil { + // 生成证据和签名 + evidence := fmt.Sprintf("Transaction ID: %d, From: %d, To: %d, Amount: %f, Time: %s, Error: %v", + originalTx.ID, originalTx.FromWalletID, originalTx.ToWalletID, + originalTx.Amount, time.Now().Format(time.RFC3339), err) + + // 使用监管者的私钥对证据进行签名 + signature, err := utils.SignData(config.GlobalConfig.Tee.PrivateKey, evidence) + if err != nil { + return err + } + + // 创建异常交易记录 + abnormal := &model.AbnormalTransaction{ + WalletID: from.ID, + TransactionID: originalTx.ID, + Evidence: evidence, + Signature: signature, + } + + if err := tx.Create(abnormal).Error; err != nil { + return err + } + + // 更新原始交易状态为失败 + originalTx.Status = TransactionStatusFailed + if err := tx.Save(originalTx).Error; err != nil { + return err + } + + return errors.ErrTransactionAmountTooLarge + } + return nil +} + +// createAssociatedTransactions 创建关联交易记录 +func createAssociatedTransactions(tx *gorm.DB, originalTx *model.Transaction) error { + // 创建加密交易记录 + encryptedTx, err := createEncryptedTransaction(originalTx) + if err != nil { + return err + } + if err := tx.Create(encryptedTx).Error; err != nil { + return err + } + + // 创建脱敏交易记录 + desensitizedTx := createDesensitizedTransaction(originalTx) + if err := tx.Create(desensitizedTx).Error; err != nil { + return err + } + + // 创建代交易记录 + proxyTxs, err := createProxyTransactions(originalTx, originalTx.Amount) + if err != nil { + return err + } + for _, proxyTx := range proxyTxs { + if err := tx.Create(proxyTx).Error; err != nil { + return err + } + } + return nil +} + +// updateWalletBalances 更新钱包余额 +func updateWalletBalances(tx *gorm.DB, from *model.Wallet, to *model.Wallet, amount float64) error { + if from.Balance < amount { + return errors.ErrInsufficientBalance + } + + from.Balance -= amount + to.Balance += amount + + if err := tx.Save(from).Error; err != nil { + return err + } + return tx.Save(to).Error +} + +// finalizeTransaction 完成交易 +func finalizeTransaction(tx *gorm.DB, originalTx *model.Transaction) error { + originalTx.Status = "completed" + return tx.Save(originalTx).Error +} + +// createProxyTransactions 创建代理交易记录 +func createProxyTransactions(originalTx *model.Transaction, amount float64) ([]*model.Transaction, error) { + // 获取代理钱包 + proxyWallets, err := GlobalWalletPool.GetRandomWallets(ProxyWalletCount) + if err != nil { + return nil, err + } + + // 拆分金额 + amounts := splitAmount(amount) + if len(amounts) != ProxyWalletCount { + return nil, fmt.Errorf("金额拆分数量与代理钱包数量不匹配") + } + + proxyTxs := make([]*model.Transaction, 0, ProxyWalletCount*2) + + // 1. 创建从原始钱包到代理钱包的交易 + for i, proxyWallet := range proxyWallets { + toProxyTx := &model.Transaction{ + FromWalletID: originalTx.FromWalletID, + ToWalletID: proxyWallet.ID, + Amount: amounts[i], + Type: model.ToProxyTransaction, + Status: "completed", + } + proxyTxs = append(proxyTxs, toProxyTx) + + // 2. 创建代理钱包到目标钱包的交易 + fromProxyTx := &model.Transaction{ + FromWalletID: proxyWallet.ID, + ToWalletID: originalTx.ToWalletID, + Amount: amounts[i], + Type: model.FromProxyTransaction, + Status: "completed", + } + proxyTxs = append(proxyTxs, fromProxyTx) + } + + return proxyTxs, nil +} + +// splitAmount 将金额拆分成多个小额 +func splitAmount(amount float64) []float64 { + // 这里实现金额拆分的逻辑 + // 示例:简单地将金额平均拆分为3份 + part := amount / 3 + return []float64{part, part, amount - 2*part} +} + +func createEncryptedTransaction(t *model.Transaction) (*model.EncryptedTransaction, error) { + // 从数据库读取公钥和私钥 + walletKey, err := GetWalletKeyByWalletID(t.FromWalletID) + if err != nil { + return nil, err + } + + // 加密交易 + encryptedFromWalletID, err := utils.RSAEncryptWithHexKey(fmt.Sprintf("%d", t.FromWalletID), walletKey.PublicKey) + if err != nil { + return nil, err + } + + encryptedToWalletID, err := utils.RSAEncryptWithHexKey(fmt.Sprintf("%d", t.ToWalletID), walletKey.PublicKey) + if err != nil { + return nil, err + } + + encryptedAmount, err := utils.RSAEncryptWithHexKey(fmt.Sprintf("%f", t.Amount), walletKey.PublicKey) + if err != nil { + return nil, err + } + + return &model.EncryptedTransaction{ + TransactionID: t.ID, + EncryptedFromWalletID: encryptedFromWalletID, + EncryptedToWalletID: encryptedToWalletID, + EncryptedAmount: encryptedAmount, + }, nil +} + +// TODO: 脱敏金额和时间范围需要根据实际情况进行调整 +func createDesensitizedTransaction(t *model.Transaction) *model.DesensitizedTransaction { + return &model.DesensitizedTransaction{ + FromWalletID: t.FromWalletID, + ToWalletID: t.ToWalletID, + AmountRange: fmt.Sprintf("%f-%f", t.Amount-10, t.Amount+10), + TimeRange: fmt.Sprintf("%d-%d", t.CreatedAt.Unix()-10, t.CreatedAt.Unix()+10), + } +} + +// GetEncryptedTransaction 获取加密交易信息 +func GetEncryptedTransaction(walletID uint, privateKey string) ([]*model.Transaction, error) { + walletKey, err := GetWalletKeyByWalletID(walletID) + if err != nil { + return nil, err + } + + if walletKey.PrivateKey != privateKey { + return nil, errors.ErrPrivateKeyInvalid + } + + // 从数据库读取 + var txs []*model.Transaction + if err := model.DB.Where("from_wallet_id = ? OR to_wallet_id = ?", walletID, walletID).Find(&txs).Error; err != nil { + return nil, err + } + + res := make([]*model.Transaction, 0) + for _, tx := range txs { + if tx.Type == model.DirectTransaction { + res = append(res, tx) + } + } + + return res, nil +} + +// GetDesensitizedTransaction 获取脱敏交易记录 +func GetDesensitizedTransaction(walletID uint) ([]*model.DesensitizedTransaction, error) { + var txs []*model.DesensitizedTransaction + if err := model.DB.Where("from_wallet_id = ? OR to_wallet_id = ?", walletID, walletID).Find(&txs).Error; err != nil { + return nil, err + } + return txs, nil +} + +// GetReceivedTransactions 获取收款记录 +func GetReceivedTransactions(walletID uint) ([]*model.Transaction, error) { + var transactions []*model.Transaction + + // 直接查询所有已完成的收款记录 + if err := model.DB.Where("to_wallet_id = ? AND status = ?", + walletID, + "completed"). + Find(&transactions).Error; err != nil { + return nil, err + } + + return transactions, nil +} + +// TransactionStats 交易统计信息 +type TransactionStats struct { + TotalAmount float64 `json:"total_amount"` // 总收入 + TodayAmount float64 `json:"today_amount"` // 今日收入 + WeekAmount float64 `json:"week_amount"` // 本周收入 + MonthAmount float64 `json:"month_amount"` // 本月收入 +} + +// GetTransactionStats 获取交易统计信息 +func GetTransactionStats(walletID uint) (*TransactionStats, error) { + var stats TransactionStats + + // 获取时间范围 + now := time.Now() + + // 今天开始时间(0点) + todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + + // 本周开始时间 + offset := int(time.Monday - now.Weekday()) + if offset > 0 { + offset = -6 + } + weekStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, offset) + + // 本月开始时间(修正:使用当前月份的第一天) + monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) + + // 查询所有的交易 + var txs []*model.Transaction + if err := model.DB.Where("to_wallet_id = ? AND type = ? AND status = ?", + walletID, model.DirectTransaction, "completed").Find(&txs).Error; err != nil { + return nil, err + } + + // 计算总收入 + for _, tx := range txs { + stats.TotalAmount += tx.Amount + } + + // 计算今日收入 + for _, tx := range txs { + if tx.CreatedAt.After(todayStart) { + stats.TodayAmount += tx.Amount + } + } + + // 计算本周收入 + for _, tx := range txs { + if tx.CreatedAt.After(weekStart) { + stats.WeekAmount += tx.Amount + } + } + + // 计算本月收入 + nextMonth := time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()) + for _, tx := range txs { + if tx.CreatedAt.After(monthStart) && tx.CreatedAt.Before(nextMonth) { + stats.MonthAmount += tx.Amount + } + } + + return &stats, nil +} + +// TransactionData 交易数据结构 +type TransactionData struct { + Date string `json:"date"` + Income float64 `json:"income"` // 收入 + Expense float64 `json:"expense"` // 支出 +} + +// TransactionTrend 交易趋势数据 +type TransactionTrend struct { + WeeklyData []TransactionData `json:"weeklyData"` + MonthlyData []TransactionData `json:"monthlyData"` +} + +// GetTrend 获取收支趋势 +func GetTrend(walletID uint) (*TransactionTrend, error) { + now := time.Now() + + monthlyData, err := getMonthlyData(walletID, now) + if err != nil { + return nil, err + } + + weeklyData, err := getWeeklyData(walletID, now) + if err != nil { + return nil, err + } + + return &TransactionTrend{ + WeeklyData: weeklyData, + MonthlyData: monthlyData, + }, nil +} + +// getMonthlyData 获取月度收支数据(最近30天) +func getMonthlyData(walletID uint, now time.Time) ([]TransactionData, error) { + var result []TransactionData + + // 获取最近30天的数据 + for i := 29; i >= 0; i-- { + currentDay := now.AddDate(0, 0, -i) + dayStart := time.Date(currentDay.Year(), currentDay.Month(), currentDay.Day(), 0, 0, 0, 0, now.Location()) + dayEnd := dayStart.AddDate(0, 0, 1) + + // 查询收入 + var income float64 + if err := model.DB.Model(&model.Transaction{}). + Select("COALESCE(SUM(amount), 0) as amount"). + Where("to_wallet_id = ? AND status = ? AND created_at >= ? AND created_at < ?", + walletID, "completed", dayStart, dayEnd). + Scan(&income).Error; err != nil { + return nil, err + } + + // 查询支出 + var expense float64 + if err := model.DB.Model(&model.Transaction{}). + Select("COALESCE(SUM(amount), 0) as amount"). + Where("from_wallet_id = ? AND status = ? AND created_at >= ? AND created_at < ?", + walletID, "completed", dayStart, dayEnd). + Scan(&expense).Error; err != nil { + return nil, err + } + + result = append(result, TransactionData{ + Date: dayStart.Format("01-02"), + Income: income, + Expense: expense, + }) + } + + return result, nil +} + +// getWeeklyData 获取周收支数据(最近7天) +func getWeeklyData(walletID uint, now time.Time) ([]TransactionData, error) { + var result []TransactionData + + // 获取最近7天的数据 + for i := 6; i >= 0; i-- { + currentDay := now.AddDate(0, 0, -i) + dayStart := time.Date(currentDay.Year(), currentDay.Month(), currentDay.Day(), 0, 0, 0, 0, now.Location()) + dayEnd := dayStart.AddDate(0, 0, 1) + + // 查询收入 + var income float64 + if err := model.DB.Model(&model.Transaction{}). + Select("COALESCE(SUM(amount), 0) as amount"). + Where("to_wallet_id = ? AND status = ? AND created_at >= ? AND created_at < ?", + walletID, "completed", dayStart, dayEnd). + Scan(&income).Error; err != nil { + return nil, err + } + + // 查询支出 + var expense float64 + if err := model.DB.Model(&model.Transaction{}). + Select("COALESCE(SUM(amount), 0) as amount"). + Where("from_wallet_id = ? AND status = ? AND created_at >= ? AND created_at < ?", + walletID, "completed", dayStart, dayEnd). + Scan(&expense).Error; err != nil { + return nil, err + } + + result = append(result, TransactionData{ + Date: dayStart.Format("01-02"), + Income: income, + Expense: expense, + }) + } + + return result, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/wallet.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/wallet.go new file mode 100644 index 000000000..e1fd3f469 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/controller/wallet.go @@ -0,0 +1,130 @@ +package controller + +import ( + "fmt" + "hufu/model" + "hufu/utils" + "time" +) + +type WalletStats struct { + TodayTransactions int64 `json:"today_transactions"` // 今日交易次数 + TotalTransactions int64 `json:"total_transactions"` // 总交易次数 +} + +func NewWallet(walletName, username string, balance float64) (*model.Wallet, error) { + w := &model.Wallet{ + WalletName: walletName, + Username: username, + Balance: balance, + } + + // 统计id数量 + var count int64 + if err := model.DB.Model(&model.Wallet{}).Count(&count).Error; err != nil { + return nil, err + } + + privateKey, publicKey := utils.GenerateRSAKey(int(count + 1)) + if privateKey == "" || publicKey == "" { + return nil, fmt.Errorf("failed to generate RSA key") + } + + if err := model.DB.Create(w).Error; err != nil { + return nil, err + } + + walletKey := &model.WalletKey{ + WalletID: w.ID, + PublicKey: publicKey, + PrivateKey: privateKey, + } + if err := model.DB.Create(walletKey).Error; err != nil { + return nil, err + } + + return w, nil +} + +func GetWalletByWalletName(walletName string) (*model.Wallet, error) { + var w model.Wallet + if err := model.DB.Where("wallet_name = ?", walletName).First(&w).Error; err != nil { + return nil, err + } + return &w, nil +} + +func GetWalletByUsername(username string) (*model.Wallet, error) { + var w model.Wallet + if err := model.DB.Where("username = ?", username).First(&w).Error; err != nil { + return nil, err + } + return &w, nil +} + +func GetWalletByID(ID uint) (*model.Wallet, error) { + var w model.Wallet + if err := model.DB.Where("id = ?", ID).First(&w).Error; err != nil { + return nil, err + } + return &w, nil +} + +func SetWalletBalance(ID uint, balance float64) error { + return model.DB.Model(&model.Wallet{}).Where("id = ?", ID).Update("balance", balance).Error +} + +func GetWalletKeyByWalletID(walletID uint) (*model.WalletKey, error) { + var wk model.WalletKey + if err := model.DB.Where("wallet_id = ?", walletID).First(&wk).Error; err != nil { + return nil, err + } + return &wk, nil +} + +// UpdateWallet 更新钱包信息 +func UpdateWallet(walletID uint, name string, balance float64) error { + // 首先检查钱包是否存在 + wallet, err := GetWalletByID(walletID) + if err != nil { + return fmt.Errorf("wallet not found: %v", err) + } + + // 更新钱包信息 + wallet.WalletName = name + wallet.Balance = balance + + // 保存更新 + if err := model.DB.Save(wallet).Error; err != nil { + return fmt.Errorf("update wallet failed: %v", err) + } + + return nil +} + +func GetWalletStats(walletID uint) (*WalletStats, error) { + // 获取今天的开始时间(零点) + now := time.Now() + todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + + var todayCount int64 + if err := model.DB.Model(&model.Transaction{}). + Where("(from_wallet_id = ? OR to_wallet_id = ?) AND created_at >= ? AND type = ?", + walletID, walletID, todayStart, model.DirectTransaction). + Count(&todayCount).Error; err != nil { + return nil, err + } + + var totalCount int64 + if err := model.DB.Model(&model.Transaction{}). + Where("(from_wallet_id = ? OR to_wallet_id = ?) AND type = ?", + walletID, walletID, model.DirectTransaction). + Count(&totalCount).Error; err != nil { + return nil, err + } + + return &WalletStats{ + TodayTransactions: todayCount, + TotalTransactions: totalCount, + }, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/db.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/db.md new file mode 100644 index 000000000..58e398474 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/db.md @@ -0,0 +1,143 @@ +# 数据库设计文档 + +## 1. 钱包相关表 + +### Wallet (钱包表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| username | varchar(100) | 是 | - | 用户名 | +| wallet_name | varchar(100) | 是 | - | 钱包名称 | +| balance | decimal(20,8) | 是 | 0 | 钱包余额 | + +### WalletKey (钱包密钥表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| wallet_id | int | 是 | - | 关联的钱包ID | +| public_key | varchar(1024) | 是 | - | 公钥 | +| private_key | varchar(1024) | 是 | - | 私钥 | + +## 2. 交易相关表 + +### Transaction (交易记录表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| from_wallet_id | int | 是 | - | 转出钱包ID | +| to_wallet_id | int | 是 | - | 转入钱包ID | +| amount | decimal(20,8) | 是 | - | 交易金额 | +| status | varchar(20) | 是 | pending | 交易状态(pending/completed/failed) | +| type | varchar(20) | 是 | - | 交易类型(direct/to_proxy/from_proxy) | + +### EncryptedTransaction (加密交易表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| transaction_id | int | 是 | - | 关联的交易ID | +| encrypted_from_wallet_id | varchar(255) | 是 | - | 加密后的转出钱包ID | +| encrypted_to_wallet_id | varchar(255) | 是 | - | 加密后的转入钱包ID | +| encrypted_amount | varchar(255) | 是 | - | 加密后的交易金额 | + +### DesensitizedTransaction (脱敏交易表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| transaction_id | int | 是 | - | 关联的交易ID | +| from_wallet_id | int | 是 | - | 转出钱包ID | +| to_wallet_id | int | 是 | - | 转入钱包ID | +| amount_range | varchar(255) | 是 | - | 交易金额范围 | +| time_range | varchar(255) | 是 | - | 交易时间范围 | + +### AbnormalTransaction (异常交易表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| wallet_id | int | 是 | - | 相关钱包ID | +| transaction_id | int | 是 | - | 异常交易ID | +| evidence | text | 是 | - | 异常证据内容 | +| signature | text | 是 | - | 监管者签名 | + +## 3. 发票相关表 + +### Invoice (发票表) +| 字段名 | 类型 | 必填 | 默认值 | 描述 | +|-------|------|------|--------|------| +| id | uint | 是 | 自增 | 主键ID | +| created_at | datetime | 是 | - | 创建时间 | +| updated_at | datetime | 是 | - | 更新时间 | +| deleted_at | datetime | 否 | null | 删除时间 | +| invoice_id | varchar(255) | 是 | - | 发票编号 | +| issue_date | varchar(255) | 是 | - | 开票日期 | +| seller_name | varchar(255) | 是 | - | 卖方名称 | +| seller_tax_id | varchar(255) | 是 | - | 卖方税号 | +| item_name | varchar(255) | 是 | - | 项目名称 | +| unit_price | decimal | 是 | - | 单价 | +| quantity | int | 是 | - | 数量 | +| amount | decimal | 是 | - | 金额 | +| tax_rate | decimal | 是 | - | 税率 | +| tax_amount | decimal | 是 | - | 税额 | +| total_amount | decimal | 是 | - | 合计金额 | +| invoice_status | varchar(255) | 是 | - | 发票状态 | +| remarks | varchar(255) | 否 | - | 备注 | +| invoice_type | varchar(255) | 是 | - | 发票类型 | + +## 4. 分页相关结构 + +### PageInfo (分页请求参数) +| 字段名 | 类型 | 描述 | +|-------|------|------| +| page | int | 页码 | +| page_size | int | 每页数量 | + +### PageResult (分页返回结果) +| 字段名 | 类型 | 描述 | +|-------|------|------| +| list | interface{} | 数据列表 | +| total | int64 | 总数 | +| page | int | 当前页码 | +| page_size | int | 每页数量 | + +## 5. 表关系说明 + +1. Wallet 与 WalletKey 是一对一关系,通过 wallet_id 关联 +2. Transaction 与 EncryptedTransaction 是一对一关系,通过 transaction_id 关联 +3. Transaction 与 DesensitizedTransaction 是一对一关系,通过 transaction_id 关联 +4. Transaction 与 AbnormalTransaction 是一对一关系,通过 transaction_id 关联 +5. Transaction 表中的 from_wallet_id 和 to_wallet_id 都关联到 Wallet 表的 id + +## 6. 字段说明补充 + +### 交易状态(status)说明 +- pending: 交易待处理 +- completed: 交易已完成 +- failed: 交易失败 + +### 交易类型(type)说明 +- direct: 直接交易 +- to_proxy: 转入代理钱包 +- from_proxy: 代理钱包转出 + +### 安全性说明 +1. WalletKey 表存储了敏感的密钥信息,需要采取额外的加密措施 +2. EncryptedTransaction 表用于存储加密后的交易信息,保护交易隐私 +3. DesensitizedTransaction 表用于存储脱敏后的交易信息,用于数据分析 +4. AbnormalTransaction 表用于记录异常交易,包含证据和监管者签名 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/docs b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/docs new file mode 160000 index 000000000..fd1eef595 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/docs @@ -0,0 +1 @@ +Subproject commit fd1eef59543a0b708560e733658aa4f29865616b diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/errors/errors.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/errors/errors.go new file mode 100644 index 000000000..8eed1b262 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/errors/errors.go @@ -0,0 +1,31 @@ +package errors + +import "fmt" + +// 定义错误类型 +type HufuError struct { + Code int + Message string +} + +func (e *HufuError) Error() string { + return fmt.Sprintf("错误码: %d, 错误信息: %s", e.Code, e.Message) +} + +var ( + ErrInsufficientBalance = &HufuError{Code: 1001, Message: "余额不足"} + ErrWalletNotFound = &HufuError{Code: 1002, Message: "钱包未找到"} + ErrTransferFailed = &HufuError{Code: 1003, Message: "转账失败"} + ErrNoProxyWallets = &HufuError{Code: 1004, Message: "没有可用的代理钱包"} + ErrPrivateKeyNotFound = &HufuError{Code: 1005, Message: "私钥未找到"} + ErrPrivateKeyInvalid = &HufuError{Code: 1006, Message: "私钥无效"} + ErrPrivateKeyApply = &HufuError{Code: 1007, Message: "私钥申请失败"} + ErrTransactionAmountTooLarge = &HufuError{Code: 1008, Message: "交易金额过大"} +) + +func NewHufuError(code int, message string) *HufuError { + return &HufuError{ + Code: code, + Message: message, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/evidence/wallet-12-20241115020658-success.txt b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/evidence/wallet-12-20241115020658-success.txt new file mode 100644 index 000000000..1e2287394 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/evidence/wallet-12-20241115020658-success.txt @@ -0,0 +1,8 @@ +异常交易记录 +------------------------ +创建时间: 2024-11-15 02:06:02 +钱包序号: 12 +交易序号: 5 +异常证据: Transaction ID: 5, From: 12, To: 11, Amount: 199999.000000, Time: 2024-11-15T02:06:02Z, Error: Amount exceeds the maximum allowed for a single transaction. +监管签名: 078fb81ca4477147ea5137c6d4ef3d89a0869f3ca0ea03f36c8d37f2a9a08823a7356ff94222df7948066f432db6fb837c6decbadb759264b50afd361c678a43 +------------------------ \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/evidence/wallet-12-20241115071644-success.txt b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/evidence/wallet-12-20241115071644-success.txt new file mode 100644 index 000000000..aebafd1f2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/evidence/wallet-12-20241115071644-success.txt @@ -0,0 +1,8 @@ +异常交易记录 +------------------------ +创建时间: 2024-11-15 07:16:12 +钱包序号: 12 +交易序号: 6 +异常证据: Transaction ID: 6, From: 12, To: 11, Amount: 199999.000000, Time: 2024-11-15T07:16:12Z, Error: Amount exceeds the maximum allowed for a single transaction. +监管签名: 51f7517b586ef3e95c22e074672eddb7e8de770377342a8bd8fc47084ae52fe10554951c2cf952ce1f8733fad38440c8888ace601772ee6a750a7864891179d7 +------------------------ \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/go.mod b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/go.mod new file mode 100644 index 000000000..93c3f20d3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/go.mod @@ -0,0 +1,71 @@ +module hufu + +go 1.23.1 + +require ( + github.com/SSSaaS/sssa-golang v0.0.0-20170502204618-d37d7782d752 + github.com/ethereum/go-ethereum v1.14.11 + github.com/gin-contrib/cors v1.7.2 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.12 +) + +require ( + github.com/FISCO-BCOS/bcos-c-sdk v0.0.0-20240726021820-a278b4749e34 // indirect + github.com/FISCO-BCOS/crypto v0.0.0-20200202032121-bd8ab0b5d4f1 // indirect + github.com/TarsCloud/TarsGo v1.4.5 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/supranational/blst v0.3.13 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) + +require ( + github.com/FISCO-BCOS/go-sdk/v3 v3.0.2 + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/gin-gonic/gin v1.10.0 + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/holiman/uint256 v1.3.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.15.0 // indirect +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/go.sum b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/go.sum new file mode 100644 index 000000000..b9438ca64 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/go.sum @@ -0,0 +1,251 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/FISCO-BCOS/bcos-c-sdk v0.0.0-20240726021820-a278b4749e34 h1:4YqD9AkbNjGw83vZzVOc4cLGBaiSNs7DppbpPDNurm8= +github.com/FISCO-BCOS/bcos-c-sdk v0.0.0-20240726021820-a278b4749e34/go.mod h1:n2KxbYa73MW3xdLVu2vpPpoblZMms+CwPmvFkubO9xM= +github.com/FISCO-BCOS/crypto v0.0.0-20200202032121-bd8ab0b5d4f1 h1:ThPht4qK10+cMZC5COIjHPq0INm5HAMVYqrez5zEgFI= +github.com/FISCO-BCOS/crypto v0.0.0-20200202032121-bd8ab0b5d4f1/go.mod h1:UrLdwsFrjiaCsvdcPLcH6B7s/FUmym3qfM93u2ziR+4= +github.com/FISCO-BCOS/go-sdk/v3 v3.0.2 h1:GvRTyudvHLeLlej4wv1uh+E/8Yyp0EHWBI7Qi2vcK/0= +github.com/FISCO-BCOS/go-sdk/v3 v3.0.2/go.mod h1:+I/+xu+rvrhUe0C1p9xh978ODtkYll5A9OV5HiFEnhg= +github.com/SSSaaS/sssa-golang v0.0.0-20170502204618-d37d7782d752 h1:NMpC6M+PtNNDYpq7ozB7kINpv10L5yeli5GJpka2PX8= +github.com/SSSaaS/sssa-golang v0.0.0-20170502204618-d37d7782d752/go.mod h1:PbJ8S5YaSYAvDPTiEuUsBHQwTUlPs6VM+Av8Oi3v570= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/TarsCloud/TarsGo v1.4.5 h1:AeOILCND6p35Swnu8MRHVLWqcjzJ+M6aKi6UIfsemNM= +github.com/TarsCloud/TarsGo v1.4.5/go.mod h1:fQITmq34rZnC0bz+KbQcHGdQUijzcmVtowXlic33jSk= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= +github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw= +github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/common.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/common.go new file mode 100644 index 000000000..8ee3d0e70 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/common.go @@ -0,0 +1,44 @@ +package handler + +import ( + "encoding/json" + "hufu/config" + "hufu/utils" + + "github.com/gin-gonic/gin" +) + +// GetEncryptionKeys 获取加密密钥的处理函数 +func GetEncryptionKeys(c *gin.Context) { + c.JSON(200, gin.H{ + "public_key": config.GlobalConfig.Tee.PublicKey, + }) +} + +func EncryptData(c *gin.Context) { + type Request struct { + FromWalletID uint `json:"from_wallet_id"` + ToWalletID uint `json:"to_wallet_id"` + Amount int `json:"amount"` + } + + var request Request + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + data, err := json.Marshal(request) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + encryptedData, err := utils.RSAEncryptWithHexKey(string(data), config.GlobalConfig.Tee.PublicKey) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + c.JSON(200, gin.H{"data": encryptedData}) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/invoice.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/invoice.go new file mode 100644 index 000000000..925e04cf7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/invoice.go @@ -0,0 +1,208 @@ +package handler + +import ( + "net/http" + + "hufu/model" + "hufu/utils" + + "github.com/gin-gonic/gin" +) + +// CreateInvoice 创建发票 +func CreateInvoice(c *gin.Context) { + var invoice model.Invoice + if err := c.ShouldBindJSON(&invoice); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求数据"}) + return + } + + // 创建发票记录 + invoice.InvoiceID = utils.GenerateRandomNumber(20) + if err := model.DB.Create(&invoice).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "创建发票失败"}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": invoice}) +} + +// GetInvoice 获取单张发票 +func GetInvoice(c *gin.Context) { + invoiceID := c.Param("id") + var invoice model.Invoice + + if err := model.DB.First(&invoice, "invoice_id = ?", invoiceID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "发票不存在"}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": invoice}) +} + +// ListInvoices 获取发票列表 +func ListInvoices(c *gin.Context) { + var pageInfo model.PageInfo + if err := c.ShouldBindQuery(&pageInfo); err != nil { + pageInfo.Page = 1 + pageInfo.PageSize = 10 + } + + var invoices []model.Invoice + var total int64 + + // 获取总数 + model.DB.Model(&model.Invoice{}).Count(&total) + + // 分页查询 + offset := (pageInfo.Page - 1) * pageInfo.PageSize + result := model.DB.Offset(offset).Limit(pageInfo.PageSize).Find(&invoices) + if result.Error != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "获取发票列表失败"}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "data": model.PageResult{ + List: invoices, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, + }) +} + +// UpdateInvoice 更新发票信息 +func UpdateInvoice(c *gin.Context) { + invoiceID := c.Param("id") + var invoice model.Invoice + + // 检查发票是否存在 + if err := model.DB.First(&invoice, "invoice_id = ?", invoiceID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "发票不存在"}) + return + } + + // 绑定更新数据 + var updateData model.Invoice + if err := c.ShouldBindJSON(&updateData); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求数据"}) + return + } + + // 更新发票信息 + if err := model.DB.Model(&invoice).Updates(updateData).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "更新发票失败"}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": invoice}) +} + +// CancelInvoice 作废发票 +func CancelInvoice(c *gin.Context) { + invoiceID := c.Param("id") + var invoice model.Invoice + + if err := model.DB.First(&invoice, "invoice_id = ?", invoiceID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "发票不存在"}) + return + } + + // 更新发票状态为作废 + if err := model.DB.Model(&invoice).Update("invoice_status", "已作废").Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "作废发票失败"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "发票已作废"}) +} + +// GetInvoiceStats 获取发票统计 +func GetInvoiceStats(c *gin.Context) { + var stats struct { + TotalCount int64 `json:"total_count"` + TotalAmount float64 `json:"total_amount"` + CancelledCount int64 `json:"cancelled_count"` + } + + // 统计总发票数和总金额 + model.DB.Model(&model.Invoice{}).Count(&stats.TotalCount) + model.DB.Model(&model.Invoice{}).Select("COALESCE(SUM(total_amount), 0)").Scan(&stats.TotalAmount) + model.DB.Model(&model.Invoice{}).Where("invoice_status = ?", "已作废").Count(&stats.CancelledCount) + + c.JSON(http.StatusOK, gin.H{"data": stats}) +} + +// VerifyInvoice 验证发票 +func VerifyInvoice(c *gin.Context) { + invoiceID := c.Param("id") + var invoice model.Invoice + + if err := model.DB.First(&invoice, "invoice_id = ?", invoiceID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "发票不存在"}) + return + } + + // 验证发票金额计算是否正确 + calculatedAmount := float64(invoice.Quantity) * invoice.UnitPrice + calculatedTaxAmount := calculatedAmount * invoice.TaxRate + calculatedTotalAmount := calculatedAmount + calculatedTaxAmount + + isValid := invoice.Amount == calculatedAmount && + invoice.TaxAmount == calculatedTaxAmount && + invoice.TotalAmount == calculatedTotalAmount + + c.JSON(http.StatusOK, gin.H{ + "is_valid": isValid, + "details": gin.H{ + "calculated_amount": calculatedAmount, + "calculated_tax_amount": calculatedTaxAmount, + "calculated_total_amount": calculatedTotalAmount, + }, + }) +} + +// DownloadInvoice 下载发票 +func DownloadInvoice(c *gin.Context) { + invoiceID := c.Param("id") + var invoice model.Invoice + + if err := model.DB.First(&invoice, "invoice_id = ?", invoiceID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "发票不存在"}) + return + } + + // TODO: 实现生成PDF文件的逻辑 + // 这里需要集成PDF生成库,如go-pdf等 + + c.Header("Content-Disposition", "attachment; filename=invoice-"+invoiceID+".pdf") + c.Header("Content-Type", "application/pdf") + // c.File(pdfPath) // 发送生成的PDF文件 +} + +// EmailInvoice 发送发票到邮箱 +func EmailInvoice(c *gin.Context) { + var req struct { + InvoiceID string `json:"invoice_id"` + Email string `json:"email"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求数据"}) + return + } + + var invoice model.Invoice + if err := model.DB.First(&invoice, "invoice_id = ?", req.InvoiceID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "发票不存在"}) + return + } + + // TODO: 实现邮件发送逻辑 + // 1. 生成PDF + // 2. 配置邮件服务 + // 3. 发送邮件 + + c.JSON(http.StatusOK, gin.H{"message": "发票已发送至邮箱"}) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/regulator.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/regulator.go new file mode 100644 index 000000000..62b21d43f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/regulator.go @@ -0,0 +1,225 @@ +package handler + +import ( + "fmt" + "hufu/controller" + "hufu/supervisor" + "log" + "net/http" + "os" + "strconv" + "time" + + "io" + "path/filepath" + "strings" + + "github.com/SSSaaS/sssa-golang" + "github.com/gin-gonic/gin" +) + +func CheckTransaction(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "message": "success", + }, + ) +} + +func GetPrivateKey(c *gin.Context) { + // 从multipart form获取数据 + walletID := c.PostForm("wallet_id") + if walletID == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "缺少wallet_id参数", + }) + return + } + + // 解析wallet_id为uint + wID, err := strconv.ParseUint(walletID, 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的wallet_id格式", + }) + return + } + + // 获取上传的文件 + file, err := c.FormFile("evidence") + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "获取evidence文件失败", + }) + return + } + + // 读取文件内容 + f, err := file.Open() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "读取文件失败", + }) + return + } + defer f.Close() + + evidence, err := io.ReadAll(f) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "读取文件内容失败", + }) + return + } + + // 解析文件内容,提取异常证据 + evidenceStr := string(evidence) + var actualEvidence string + + fileName := fmt.Sprintf("wallet-%d-%s-failed", wID, time.Now().Format("20060102150405")) + filePath := fmt.Sprintf("./evidence/%s.txt", fileName) + err = os.WriteFile(filePath, evidence, 0644) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "保存证据文件失败", + }) + return + } + + // 按行分割文本 + lines := strings.Split(evidenceStr, "\n") + for i := 0; i < len(lines); i++ { + line := strings.TrimSpace(lines[i]) + if strings.HasPrefix(line, "异常证据:") || strings.HasPrefix(line, "异常证据:") { + actualEvidence = strings.TrimSpace(strings.TrimPrefix(strings.TrimPrefix(line, "异常证据:"), "异常证据:")) + break + } + } + + if actualEvidence == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "文件格式错误:未找到异常证据", + }) + return + } + + log.Println("actualEvidence", actualEvidence) + + // 调用处理函数,使用解析出的异常证据 + res, err := controller.ProcessPrivateKey(uint(wID), actualEvidence) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "处理私钥失败: " + err.Error(), + }) + return + } + + pk, err := sssa.Combine(res) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "处理私钥失败: " + err.Error(), + }) + return + } + + // 重命名文件 + fileName = fmt.Sprintf("wallet-%d-%s-success", wID, time.Now().Format("20060102150405")) + os.Rename(filePath, fmt.Sprintf("./evidence/%s.txt", fileName)) + + c.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": pk, + "shares": res, + }) +} + +// 获取异常交易 +func GetAbnormalTransaction(c *gin.Context) { + // 从数据库获取异常交易列表 + abnormalTxs, err := controller.GetAbnormalTransactions() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": -1, + "message": "获取异常交易失败", + "error": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "message": "success", + "data": abnormalTxs, + }) +} + +// 获取决策 +func GetDecision(c *gin.Context) { + decision, err := supervisor.GetDecision() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + } + + c.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": decision, + }) +} + +// 获取事件 +func GetEvent(c *gin.Context) { + event, err := supervisor.GetEvent() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + } + + c.JSON(http.StatusOK, event) +} + +// GetApplication 获取申请记录 +func GetApplication(c *gin.Context) { + // 查找匹配的文件 + files, err := filepath.Glob("./evidence/wallet-*") + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "查找文件失败"}) + return + } + + applications := make([]map[string]interface{}, 0) + for _, file := range files { + // 读取文件内容 + content, err := os.ReadFile(file) + if err != nil { + continue + } + + // 从文件名解析信息 + fileName := filepath.Base(file) + // 移除.txt后缀 + fileName = strings.TrimSuffix(fileName, ".txt") + parts := strings.Split(fileName, "-") + if len(parts) < 4 { + continue + } + + // 构建返回数据 + application := map[string]interface{}{ + "wallet_id": parts[1], // 钱包ID (12) + "timestamp": parts[2], // 时间戳 (20241107124506) + "status": strings.TrimSuffix(parts[3], ".txt"), // 状态 (success/failed) + "content": string(content), + } + applications = append(applications, application) + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "msg": "success", + "data": applications, + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/tee.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/tee.go new file mode 100644 index 000000000..be82bf1ac --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/tee.go @@ -0,0 +1,289 @@ +package handler + +import ( + "hufu/controller" + "hufu/model" + "hufu/utils" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +type TeeHandler struct { + TeeController *controller.TeeController +} + +func NewTeeHandler() *TeeHandler { + return &TeeHandler{ + TeeController: controller.NewTeeController(), + } +} + +func (h *TeeHandler) TeeAdd(c *gin.Context) { + var requestBody struct { + Add string `json:"add"` + } + + if err := c.ShouldBindJSON(&requestBody); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "解析 JSON 请求失败: " + err.Error(), + }) + return + } + + respBody, statusCode, err := h.TeeController.Add(requestBody.Add) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + c.String(statusCode, string(respBody)) +} + +func (h *TeeHandler) TeeGenerateKey(c *gin.Context) { + var request struct { + WalletID int `json:"wallet_id"` + } + + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + if request.WalletID == 0 { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "wallet_id 不能为空", + }) + return + } + + respBody, statusCode, err := h.TeeController.GenerateKey(request.WalletID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + c.String(statusCode, string(respBody)) +} + +func (h *TeeHandler) TeeShuffle(c *gin.Context) { + var request struct { + From int `json:"from"` + To int `json:"to"` + Amount float64 `json:"amount"` + } + + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + respBody, statusCode, err := h.TeeController.Shuffle(request.From, request.To, request.Amount) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + c.String(statusCode, string(respBody)) +} + +func (h *TeeHandler) TeeWarning(c *gin.Context) { + var request struct { + From int `json:"from"` + To int `json:"to"` + Amount float64 `json:"amount"` + } + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + respBody, statusCode, err := h.TeeController.Warning(request.From, request.To, request.Amount) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + c.String(statusCode, string(respBody)) +} + +func (h *TeeHandler) TeeDecrypt(c *gin.Context) { + var request struct { + From string `json:"from"` + To string `json:"to"` + Amount string `json:"amount"` + } + + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + respBody, statusCode, err := h.TeeController.Decrypt(request.From, request.To, request.Amount) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + c.String(statusCode, string(respBody)) +} + +func (h *TeeHandler) TeeEncrypt(c *gin.Context) { + var request struct { + From string `json:"from"` + To string `json:"to"` + Amount string `json:"amount"` + } + + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + encryptedData, err := h.TeeController.Encrypt(request.From, request.To, request.Amount) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, encryptedData) +} + +func (h *TeeHandler) GetEncryptedHistory(c *gin.Context) { + var req struct { + Id string `json:"id"` + PrivateKey string `json:"private_key"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + // 验证私钥是否正确 + wallet, err := controller.GetWalletByID(utils.StringToUint(req.Id)) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": "钱包不存在: " + err.Error(), + }) + return + } + walletKey, err := controller.GetWalletKeyByWalletID(wallet.ID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": "钱包密钥不存在: " + err.Error(), + }) + return + } + + // 验证私钥是否匹配 + if walletKey.PrivateKey != req.PrivateKey { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "私钥验证失败", + }) + return + } + var result []model.EncryptedTransaction + if err := model.DB.Find(&result).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + c.JSON(http.StatusOK, result) +} + +func (h *TeeHandler) GetDecryptedHistory(c *gin.Context) { + var req struct { + Id string `json:"id"` + PrivateKey string `json:"private_key"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无效的请求格式: " + err.Error(), + }) + return + } + + // 验证私钥是否正确 + wallet, err := controller.GetWalletByID(utils.StringToUint(req.Id)) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": "钱包不存在: " + err.Error(), + }) + return + } + walletKey, err := controller.GetWalletKeyByWalletID(wallet.ID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": "钱包密钥不存在: " + err.Error(), + }) + return + } + + // 验证私钥是否匹配 + if walletKey.PrivateKey != req.PrivateKey { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "私钥验证失败", + }) + return + } + + type DecryptedTransaction struct { + From string `json:"from"` + To string `json:"to"` + Amount string `json:"amount"` + CreateAt string `json:"create_at"` + } + var result []model.EncryptedTransaction + if err := model.DB.Find(&result).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "请求失败: " + err.Error(), + }) + return + } + + var resp []DecryptedTransaction + for _, item := range result { + from, _ := utils.RSADecryptWithHexKey(item.EncryptedFromWalletID, walletKey.PrivateKey, walletKey.PublicKey) + to, _ := utils.RSADecryptWithHexKey(item.EncryptedToWalletID, walletKey.PrivateKey, walletKey.PublicKey) + amount, _ := utils.RSADecryptWithHexKey(item.EncryptedAmount, walletKey.PrivateKey, walletKey.PublicKey) + createAt := item.CreatedAt.Format(time.DateTime) + decrypted := DecryptedTransaction{ + From: from, + To: to, + Amount: amount, + CreateAt: createAt, + } + resp = append(resp, decrypted) + } + + c.JSON(http.StatusOK, resp) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/transaction.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/transaction.go new file mode 100644 index 000000000..25d3f2153 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/transaction.go @@ -0,0 +1,407 @@ +package handler + +import ( + "encoding/json" + "fmt" + "hufu/config" + "hufu/controller" + "hufu/model" + "hufu/utils" + "log" + "net/http" + "strconv" + "time" + + "github.com/gin-gonic/gin" +) + +type EncryptFTA struct { + From string `json:"from"` + To string `json:"to"` + Amount string `json:"amount"` +} + +type DecryptFTA struct { + From int `json:"from"` + To int `json:"to"` + Amount float64 `json:"amount"` +} + +type ResDecrypt struct { + Message string `json:"message"` + Data DecryptFTA `json:"data"` +} + +type ResWarning struct { + TransactionStatus string `json:"transaction_status"` + WarningMessage string `json:"warning_msg"` +} + +type ResShuffle struct { + Data map[string]float64 `json:"data"` +} + +// NormalTransfer 处理转账请求 +func NormalTransfer(c *gin.Context) { + var req model.Transaction + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + from, err := controller.GetWalletByID(req.FromWalletID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + to, err := controller.GetWalletByID(req.ToWalletID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + + tx, err := controller.NormalTransfer(from, to, req.Amount) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": tx}) +} + +// ProxyTransfer 处理转账请求 +func ProxyTransfer(c *gin.Context) { + var req EncryptFTA + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + tc := controller.NewTeeController() + + // 1. 解密交易数据 + decryptedData, err := handleDecryption(tc, req) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 2. 保存EncryptFTA + if err := createEncryptedTransaction(decryptedData); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 3. 验证交易 + if err := handleWarningCheck(tc, decryptedData); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 4. 混洗交易 + shuffleResult, err := handleShuffle(tc, decryptedData) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 5. 执行源钱包到代理钱包的转账 + if err := handleSourceToProxy(decryptedData); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 6. 执行代理到目标钱包的转账 + if err := handleProxyToTarget(decryptedData, shuffleResult); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "success"}) +} + +// createEncryptedTransaction 创建加密交易记录 +func createEncryptedTransaction(decryptedData *DecryptFTA) error { + walletKey, err := controller.GetWalletKeyByWalletID(uint(decryptedData.From)) + if err != nil { + return err + } + + encryptedFromWalletID, err := utils.RSAEncryptWithHexKey(strconv.Itoa(decryptedData.From), walletKey.PublicKey) + if err != nil { + return err + } + + encryptedToWalletID, err := utils.RSAEncryptWithHexKey(strconv.Itoa(decryptedData.To), walletKey.PublicKey) + if err != nil { + return err + } + + encryptedAmount, err := utils.RSAEncryptWithHexKey(strconv.FormatFloat(decryptedData.Amount, 'f', -1, 64), walletKey.PublicKey) + if err != nil { + return err + } + + var count int64 + if err := model.DB.Model(&model.Transaction{}).Count(&count).Error; err != nil { + return err + } + + encryptedTx := &model.EncryptedTransaction{ + TransactionID: uint(count + 1), + EncryptedFromWalletID: encryptedFromWalletID, + EncryptedToWalletID: encryptedToWalletID, + EncryptedAmount: encryptedAmount, + } + + return model.DB.Create(encryptedTx).Error +} + +// handleDecryption 处理解密过程 +func handleDecryption(tc *controller.TeeController, req EncryptFTA) (*DecryptFTA, error) { + respBody, _, _ := tc.Decrypt(req.From, req.To, req.Amount) + log.Println(string(respBody)) + + var resDecrypt ResDecrypt + if err := json.Unmarshal(respBody, &resDecrypt); err != nil { + return nil, err + } + + if resDecrypt.Message != "Transaction decrypted successfully." { + return nil, fmt.Errorf(resDecrypt.Message) + } + + return &resDecrypt.Data, nil +} + +// handleWarningCheck 处理交易验证 +func handleWarningCheck(tc *controller.TeeController, decryptedData *DecryptFTA) error { + respBody, _, _ := tc.Warning(decryptedData.From, decryptedData.To, decryptedData.Amount) + log.Println("warning:", string(respBody)) + + var resWarning ResWarning + if err := json.Unmarshal(respBody, &resWarning); err != nil { + return err + } + + if resWarning.TransactionStatus != "Success" { + log.Println("warning:================================================") + log.Println(resWarning.WarningMessage) + if err := createAbnormalTransaction(decryptedData, resWarning.WarningMessage); err != nil { + return err + } + return fmt.Errorf(resWarning.WarningMessage) + } + + return nil +} + +// createAbnormalTransaction 创建异常交易记录 +func createAbnormalTransaction(decryptedData *DecryptFTA, warningMessage string) error { + var count int64 + if err := model.DB.Model(&model.Transaction{}).Count(&count).Error; err != nil { + return err + } + + evidence := fmt.Sprintf("Transaction ID: %d, From: %d, To: %d, Amount: %f, Time: %s, Error: %v", + count+1, decryptedData.From, decryptedData.To, decryptedData.Amount, + time.Now().Format(time.RFC3339), warningMessage) + + // 使用监管者的私钥对证据进行签名 + signature, err := utils.SignData(config.GlobalConfig.Tee.PrivateKey, evidence) + if err != nil { + return err + } + + abnormal := &model.AbnormalTransaction{ + WalletID: uint(decryptedData.From), + TransactionID: uint(count + 1), + Evidence: evidence, + Signature: signature, + } + + return model.DB.Create(abnormal).Error +} + +// handleShuffle 处理混洗过程 +func handleShuffle(tc *controller.TeeController, decryptedData *DecryptFTA) (*ResShuffle, error) { + respBody, _, _ := tc.Shuffle(decryptedData.From, decryptedData.To, decryptedData.Amount) + log.Println("shuffle:", string(respBody)) + + var resShuffle ResShuffle + if err := json.Unmarshal(respBody, &resShuffle); err != nil { + return nil, err + } + + return &resShuffle, nil +} + +// handleSourceToProxy 处理源钱包到代理钱包的转账 +func handleSourceToProxy(decryptedData *DecryptFTA) error { + from, err := controller.GetWalletByID(uint(decryptedData.From)) + if err != nil { + return err + } + + proxy, err := controller.GetWalletByID(2) + if err != nil { + return err + } + + _, err = controller.NormalTransfer(from, proxy, decryptedData.Amount) + return err +} + +// handleProxyToTarget 处理代理到目标钱包的转账 +func handleProxyToTarget(decryptedData *DecryptFTA, shuffleResult *ResShuffle) error { + to, err := controller.GetWalletByID(uint(decryptedData.To)) + if err != nil { + return err + } + + for from, amount := range shuffleResult.Data { + id, _ := strconv.Atoi(from) + fromWallet, err := controller.GetWalletByID(uint(id)) + if err != nil { + return err + } + + if _, err := controller.NormalTransfer(fromWallet, to, amount); err != nil { + return err + } + } + + return nil +} + +// GetTransferHistory 获取转账历史 +func GetTransferHistory(c *gin.Context) { + var req struct { + WalletID uint `json:"wallet_id" binding:"required"` + Page int `json:"page"` + PageSize int `json:"page_size"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 设置默认值 + if req.Page <= 0 { + req.Page = 1 + } + if req.PageSize <= 0 { + req.PageSize = 10 + } + + result, err := controller.GetTransferHistory(req.WalletID, req.Page, req.PageSize) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "data": result, + }) +} + +// GetEncryptedTransaction 获取加密交易信息 +// func GetEncryptedTransaction(c *gin.Context) { +// var req Req +// if err := c.ShouldBindJSON(&req); err != nil { +// c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) +// return +// } + +// encryptedTx, err := controller.GetEncryptedTransaction(req.WalletID, req.PrivateKey) +// if err != nil { +// c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) +// return +// } + +// c.JSON(http.StatusOK, gin.H{ +// "code": 0, +// "data": encryptedTx, +// }) +// } + +// GetDesensitizedTransaction 获取脱敏交易记录 +// func GetDesensitizedTransaction(c *gin.Context) { +// var req Req +// if err := c.ShouldBindJSON(&req); err != nil { +// c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) +// return +// } + +// transactions, err := controller.GetDesensitizedTransaction(req.WalletID) +// if err != nil { +// c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) +// return +// } + +// c.JSON(http.StatusOK, gin.H{ +// "data": transactions, +// }) +// } + +// GetReceivedTransactions 获取收款记录 +func GetReceivedTransactions(c *gin.Context) { + var req struct { + WalletID uint `json:"wallet_id" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + result, err := controller.GetReceivedTransactions(req.WalletID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "data": result, + }) +} + +// GetTransactionStats 获取交易统计信息 +func GetTransactionStats(c *gin.Context) { + var req struct { + WalletID uint `json:"wallet_id" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": -1, + "msg": "参数错误: " + err.Error(), + }) + return + } + + stats, err := controller.GetTransactionStats(req.WalletID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": -1, + "msg": "获取统计信息失败: " + err.Error(), + }) + return + } + + // 在返回结果之前对数据进行格式化处理 + formattedStats := gin.H{ + "total_amount": utils.FormatFloat(stats.TotalAmount, 2), + "today_amount": utils.FormatFloat(stats.TodayAmount, 2), + "week_amount": utils.FormatFloat(stats.WeekAmount, 2), + "month_amount": utils.FormatFloat(stats.MonthAmount, 2), + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "data": formattedStats, + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/wallet.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/wallet.go new file mode 100644 index 000000000..ae67e9a0a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/handler/wallet.go @@ -0,0 +1,144 @@ +package handler + +import ( + "hufu/controller" + "hufu/model" + "net/http" + + "github.com/gin-gonic/gin" +) + +// CreateWallet 创建钱包 +func CreateWallet(c *gin.Context) { + var req model.Wallet + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + wallet, err := controller.NewWallet(req.WalletName, req.Username, req.Balance) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": wallet}) +} + +// GetWallet 获取单个钱包详情 +func GetWallet(c *gin.Context) { + var req model.Wallet + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if req.ID != 0 { + wallet, err := controller.GetWalletByID(req.ID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"data": wallet}) + return + } + if req.WalletName != "" { + wallet, err := controller.GetWalletByWalletName(req.WalletName) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"data": wallet}) + + } + if req.Username != "" { + wallet, err := controller.GetWalletByUsername(req.Username) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"data": wallet}) + } + +} + +// UpdateWallet 更新钱包信息 +func UpdateWallet(c *gin.Context) { + var req model.Wallet + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": -1, + "msg": "参数错误: " + err.Error(), + }) + return + } + + // 调用 controller 层更新钱包 + err := controller.UpdateWallet(req.ID, req.WalletName, req.Balance) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": -1, + "msg": "更新钱包失败: " + err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "msg": "更新钱包成功", + }) +} + +// GetWalletStats 获取钱包统计信息 +func GetWalletStats(c *gin.Context) { + var req struct { + WalletID uint `json:"wallet_id" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + stats, err := controller.GetWalletStats(req.WalletID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": stats}) +} + +// GetTrend 获取收支趋势 +func GetTrend(c *gin.Context) { + var req struct { + WalletID uint `json:"wallet_id" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": -1, + "message": "参数错误", + "error": err.Error(), + }) + return + } + + // 从数据库获取支出趋势数据 + trends, err := controller.GetTrend(req.WalletID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": -1, + "message": "获取支出趋势失败", + "error": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "message": "success", + "data": trends, + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/main.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/main.go new file mode 100644 index 000000000..efc3cd6c3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "hufu/config" + "hufu/controller" + "hufu/model" + "hufu/router" + "hufu/supervisor" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" +) + +func main() { + if _, err := config.LoadConfig("config/config.yaml"); err != nil { + panic(fmt.Sprintf("Error loading config: %v", err)) + } + model.SetupDB() + controller.InitWalletPool() + supervisor.InitJury() + r := gin.Default() + r.Use(cors.New(cors.Config{ + AllowOrigins: []string{"*"}, + AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, + AllowHeaders: []string{"Origin", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"}, + ExposeHeaders: []string{"Content-Length"}, + AllowCredentials: true, + })) + + r.GET("/ping", func(c *gin.Context) { + c.JSON(200, gin.H{"message": "pong"}) + }) + + router.InitHufuRouter(r) + router.InitRegulatorRouter(r) + r.Run(":3338") +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/common.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/common.go new file mode 100644 index 000000000..7c8bb2262 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/common.go @@ -0,0 +1,36 @@ +package model + +import "gorm.io/gorm" + +// PageInfo 分页请求参数 +type PageInfo struct { + Page int `json:"page" form:"page"` // 页码 + PageSize int `json:"page_size" form:"page_size"` // 每页数量 +} + +// PageResult 分页返回结果 +type PageResult struct { + List interface{} `json:"list"` // 数据列表 + Total int64 `json:"total"` // 总数 + Page int `json:"page"` // 当前页码 + PageSize int `json:"page_size"` // 每页数量 +} + +// Invoice 发票结构体 +type Invoice struct { + gorm.Model + InvoiceID string `json:"invoice_id"` // 发票编号 + IssueDate string `json:"issue_date"` // 开票日期 + SellerName string `json:"seller_name"` // 卖方名称 + SellerTaxID string `json:"seller_tax_id"` // 卖方税号 + ItemName string `json:"item_name"` // 项目名称 + UnitPrice float64 `json:"unit_price"` // 单价 + Quantity int `json:"quantity"` // 数量 + Amount float64 `json:"amount"` // 金额 + TaxRate float64 `json:"tax_rate"` // 税率 + TaxAmount float64 `json:"tax_amount"` // 税额 + TotalAmount float64 `json:"total_amount"` // 合计金额 + InvoiceStatus string `json:"invoice_status"` // 发票状态 + Remarks string `json:"remarks"` // 备注 + InvoiceType string `json:"invoice_type"` // 发票类型 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/db.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/db.go new file mode 100644 index 000000000..fba5fe14c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/db.go @@ -0,0 +1,45 @@ +package model + +import ( + "fmt" + "hufu/config" + + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func SetupDB() *gorm.DB { + cfg := config.GlobalConfig.Database + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", + cfg.Username, + cfg.Password, + cfg.Host, + cfg.Port, + cfg.DBName, + cfg.Charset, + ) + + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + + // 按照依赖关系顺序进行迁移 + err = db.AutoMigrate( + &Wallet{}, + &WalletKey{}, + &Transaction{}, + &EncryptedTransaction{}, + &DesensitizedTransaction{}, + &AbnormalTransaction{}, + &Invoice{}, + ) + if err != nil { + panic("failed to auto migrate: " + err.Error()) + } + + DB = db + return db +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/transaction.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/transaction.go new file mode 100644 index 000000000..3e20477a3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/transaction.go @@ -0,0 +1,52 @@ +package model + +import ( + "gorm.io/gorm" +) + +// TransactionType 交易类型 +type TransactionType string + +const ( + DirectTransaction TransactionType = "direct" // 直接交易 + ToProxyTransaction TransactionType = "to_proxy" // 转入代理钱包 + FromProxyTransaction TransactionType = "from_proxy" // 代理钱包转出 +) + +// Transaction 交易记录 +type Transaction struct { + gorm.Model + FromWalletID uint `json:"from_wallet_id" gorm:"type:int;not null"` + ToWalletID uint `json:"to_wallet_id" gorm:"type:int;not null"` + Amount float64 `json:"amount" gorm:"type:decimal(20,8);not null"` + Status string `json:"status" gorm:"type:varchar(20);not null;default:'pending'"` // pending, completed, failed + Type TransactionType `json:"type" gorm:"type:varchar(20);not null"` // direct, proxy, mixed +} + +// EncryptedTransaction 加密交易 +type EncryptedTransaction struct { + gorm.Model + TransactionID uint `json:"transaction_id" gorm:"type:int;not null"` + EncryptedFromWalletID string `json:"encrypted_from_wallet_id"` + EncryptedToWalletID string `json:"encrypted_to_wallet_id"` + EncryptedAmount string `json:"encrypted_amount"` +} + +// DesensitizedTransaction 脱敏交易 +type DesensitizedTransaction struct { + gorm.Model + TransactionID uint `json:"transaction_id" gorm:"type:int;not null"` + FromWalletID uint `json:"from_wallet_id"` + ToWalletID uint `json:"to_wallet_id"` + AmountRange string `json:"amount_range"` + TimeRange string `json:"time_range"` +} + +// AbnormalTransaction 异常交易 +type AbnormalTransaction struct { + gorm.Model + WalletID uint `json:"wallet_id" gorm:"type:int;not null"` + TransactionID uint `json:"transaction_id" gorm:"type:int;not null"` + Evidence string `json:"evidence" gorm:"type:text;not null"` // 证据内容 + Signature string `json:"signature" gorm:"type:text;not null"` // 监管者签名 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/wallet.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/wallet.go new file mode 100644 index 000000000..bfc6ae26c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/model/wallet.go @@ -0,0 +1,19 @@ +package model + +import "gorm.io/gorm" + +// Wallet 钱包 +type Wallet struct { + gorm.Model + Username string `json:"user_name" gorm:"type:varchar(100);not null"` // 用户名 + WalletName string `json:"wallet_name" gorm:"type:varchar(100);not null"` // 钱包名称 + Balance float64 `json:"balance" gorm:"type:decimal(20,8);default:0"` // 余额 +} + +// 钱包公私钥 +type WalletKey struct { + gorm.Model + WalletID uint `json:"wallet_id" gorm:"type:int;not null"` + PublicKey string `json:"public_key" gorm:"type:varchar(1024);not null"` + PrivateKey string `json:"private_key" gorm:"type:varchar(1024);not null"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/router/router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/router/router.go new file mode 100644 index 000000000..7b773a29d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/router/router.go @@ -0,0 +1,80 @@ +package router + +import ( + "hufu/handler" + + "github.com/gin-gonic/gin" +) + +func InitHufuRouter(r *gin.Engine) { + hufu := r.Group("/api/v1/hufu") + { + // 钱包相关路由 + wallet := hufu.Group("/wallet") + { + wallet.POST("/create", handler.CreateWallet) // 创建钱包 + wallet.POST("/", handler.GetWallet) // 获取单个钱包 + wallet.POST("/update", handler.UpdateWallet) // 更新钱包 + wallet.POST("/stats", handler.GetWalletStats) // 获取钱包统计 + wallet.POST("/trend", handler.GetTrend) // 获取收支趋势 + } + + // 转账相关路由 + tx := hufu.Group("/tx") + { + tx.POST("/normal-transfer", handler.NormalTransfer) // 普通转账 + tx.POST("/proxy-transfer", handler.ProxyTransfer) // 代理转账 + // tx.POST("/encrypt", handler.EncryptData) // 加密数据 + // tx.POST("/encrypted-transfer", handler.EncryptedTransfer) // 加密转账 + tx.POST("/history", handler.GetTransferHistory) // 获取转账历史 + // tx.POST("/desensitized", handler.GetDesensitizedTransaction) // 获取脱敏交易记录 + tx.POST("/received", handler.GetReceivedTransactions) // 添加获取收款记录路由 + tx.POST("/stats", handler.GetTransactionStats) // 添加获取收入统计路由 + } + + // 发票相关路由 + invoice := hufu.Group("/invoice") + { + invoice.POST("/create", handler.CreateInvoice) // 创建发票 + invoice.POST("/get", handler.GetInvoice) // 获取单张发票 + invoice.POST("/list", handler.ListInvoices) // 获取发票列表 + invoice.POST("/update", handler.UpdateInvoice) // 更新发票信息 + invoice.POST("/cancel", handler.CancelInvoice) // 作废发票 + invoice.POST("/stats", handler.GetInvoiceStats) // 获取发票统计 + invoice.POST("/verify", handler.VerifyInvoice) // 验证发票 + invoice.POST("/download", handler.DownloadInvoice) // 下载发票 + invoice.POST("/email", handler.EmailInvoice) // 发送发票到邮箱 + } + + // 密钥相关路由 + keys := hufu.Group("/keys") + { + keys.POST("/", handler.GetEncryptionKeys) // 获取加密密钥 + } + + teeHandler := handler.NewTeeHandler() + tee := hufu.Group("/tee") + { + tee.POST("/add", teeHandler.TeeAdd) + tee.POST("/generate-key", teeHandler.TeeGenerateKey) + tee.POST("/encrypt-transaction", teeHandler.TeeEncrypt) + tee.POST("/decrypt-transaction", teeHandler.TeeDecrypt) + tee.POST("/shuffle", teeHandler.TeeShuffle) + tee.POST("/warning", teeHandler.TeeWarning) + tee.POST("/encrypted-history", teeHandler.GetEncryptedHistory) // 获取加密交易记录 + tee.POST("/decrypt-history", teeHandler.GetDecryptedHistory) // 获取解密交易记录 + } + } +} + +func InitRegulatorRouter(r *gin.Engine) { + regulator := r.Group("/api/v1/regulator") + { + regulator.POST("/alert", handler.CheckTransaction) // 检查交易 + regulator.POST("/private-key", handler.GetPrivateKey) // 获取私钥 + regulator.POST("/application", handler.GetApplication) // 获取申请记录 + regulator.POST("/abnormal", handler.GetAbnormalTransaction) // 获取异常交易 + regulator.POST("/decision", handler.GetDecision) // 获取决策 + regulator.POST("/event", handler.GetEvent) // 获取事件 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/script b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/script new file mode 160000 index 000000000..d051bb815 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/script @@ -0,0 +1 @@ +Subproject commit d051bb815063bdc2a3aa942e79b4aac1a190c9d1 diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/.gitignore b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/.gitignore new file mode 100644 index 000000000..4ef27174d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/.gitignore @@ -0,0 +1,2 @@ + +*.log diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/Dockerfile b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/Dockerfile new file mode 100644 index 000000000..69cb44dc6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:alpine as builder + +WORKDIR /go/src/github.com/flipped-aurora/gin-vue-admin/server +COPY . . + +RUN go env -w GO111MODULE=on \ + && go env -w GOPROXY=https://goproxy.cn,direct \ + && go env -w CGO_ENABLED=0 \ + && go env \ + && go mod tidy \ + && go build -o server . + +FROM alpine:latest + +LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com" +# 设置时区 +ENV TZ=Asia/Shanghai +RUN apk update && apk add --no-cache tzdata openntpd \ + && ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +WORKDIR /go/src/github.com/flipped-aurora/gin-vue-admin/server + +COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server/server ./ +COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server/resource ./resource/ +COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.docker.yaml ./ + +# 挂载目录:如果使用了sqlite数据库,容器命令示例:docker run -d -v /宿主机路径/gva.db:/go/src/github.com/flipped-aurora/gin-vue-admin/server/gva.db -p 8888:8888 --name gva-server-v1 gva-server:1.0 +# VOLUME ["/go/src/github.com/flipped-aurora/gin-vue-admin/server"] + +EXPOSE 8888 +ENTRYPOINT ./server -c config.docker.yaml diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/README.md new file mode 100644 index 000000000..9a34870bc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/README.md @@ -0,0 +1,54 @@ +## server项目结构 + +```shell +├── api +│   └── v1 +├── config +├── core +├── docs +├── global +├── initialize +│   └── internal +├── middleware +├── model +│   ├── request +│   └── response +├── packfile +├── resource +│   ├── excel +│   ├── page +│   └── template +├── router +├── service +├── source +└── utils + ├── timer + └── upload +``` + +| 文件夹 | 说明 | 描述 | +| ------------ | ----------------------- | --------------------------- | +| `api` | api层 | api层 | +| `--v1` | v1版本接口 | v1版本接口 | +| `config` | 配置包 | config.yaml对应的配置结构体 | +| `core` | 核心文件 | 核心组件(zap, viper, server)的初始化 | +| `docs` | swagger文档目录 | swagger文档目录 | +| `global` | 全局对象 | 全局对象 | +| `initialize` | 初始化 | router,redis,gorm,validator, timer的初始化 | +| `--internal` | 初始化内部函数 | gorm 的 longger 自定义,在此文件夹的函数只能由 `initialize` 层进行调用 | +| `middleware` | 中间件层 | 用于存放 `gin` 中间件代码 | +| `model` | 模型层 | 模型对应数据表 | +| `--request` | 入参结构体 | 接收前端发送到后端的数据。 | +| `--response` | 出参结构体 | 返回给前端的数据结构体 | +| `packfile` | 静态文件打包 | 静态文件打包 | +| `resource` | 静态资源文件夹 | 负责存放静态文件 | +| `--excel` | excel导入导出默认路径 | excel导入导出默认路径 | +| `--page` | 表单生成器 | 表单生成器 打包后的dist | +| `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 | +| `router` | 路由层 | 路由层 | +| `service` | service层 | 存放业务逻辑问题 | +| `source` | source层 | 存放初始化数据的函数 | +| `utils` | 工具包 | 工具函数封装 | +| `--timer` | timer | 定时器接口封装 | +| `--upload` | oss | oss接口封装 | + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/enter.go new file mode 100644 index 000000000..5c1dff443 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/enter.go @@ -0,0 +1,13 @@ +package v1 + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/api/v1/example" + "github.com/flipped-aurora/gin-vue-admin/server/api/v1/system" +) + +var ApiGroupApp = new(ApiGroup) + +type ApiGroup struct { + SystemApiGroup system.ApiGroup + ExampleApiGroup example.ApiGroup +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/enter.go new file mode 100644 index 000000000..c182328ea --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/enter.go @@ -0,0 +1,13 @@ +package example + +import "github.com/flipped-aurora/gin-vue-admin/server/service" + +type ApiGroup struct { + CustomerApi + FileUploadAndDownloadApi +} + +var ( + customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService + fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_breakpoint_continue.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_breakpoint_continue.go new file mode 100644 index 000000000..8f39cb160 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_breakpoint_continue.go @@ -0,0 +1,150 @@ +package example + +import ( + "fmt" + "io" + "mime/multipart" + "strconv" + + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// BreakpointContinue +// @Tags ExaFileUploadAndDownload +// @Summary 断点续传到服务器 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param file formData file true "an example for breakpoint resume, 断点续传示例" +// @Success 200 {object} response.Response{msg=string} "断点续传到服务器" +// @Router /fileUploadAndDownload/breakpointContinue [post] +func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) { + fileMd5 := c.Request.FormValue("fileMd5") + fileName := c.Request.FormValue("fileName") + chunkMd5 := c.Request.FormValue("chunkMd5") + chunkNumber, _ := strconv.Atoi(c.Request.FormValue("chunkNumber")) + chunkTotal, _ := strconv.Atoi(c.Request.FormValue("chunkTotal")) + _, FileHeader, err := c.Request.FormFile("file") + if err != nil { + global.GVA_LOG.Error("接收文件失败!", zap.Error(err)) + response.FailWithMessage("接收文件失败", c) + return + } + f, err := FileHeader.Open() + if err != nil { + global.GVA_LOG.Error("文件读取失败!", zap.Error(err)) + response.FailWithMessage("文件读取失败", c) + return + } + defer func(f multipart.File) { + err := f.Close() + if err != nil { + fmt.Println(err) + } + }(f) + cen, _ := io.ReadAll(f) + if !utils.CheckMd5(cen, chunkMd5) { + global.GVA_LOG.Error("检查md5失败!", zap.Error(err)) + response.FailWithMessage("检查md5失败", c) + return + } + file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal) + if err != nil { + global.GVA_LOG.Error("查找或创建记录失败!", zap.Error(err)) + response.FailWithMessage("查找或创建记录失败", c) + return + } + pathC, err := utils.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5) + if err != nil { + global.GVA_LOG.Error("断点续传失败!", zap.Error(err)) + response.FailWithMessage("断点续传失败", c) + return + } + + if err = fileUploadAndDownloadService.CreateFileChunk(file.ID, pathC, chunkNumber); err != nil { + global.GVA_LOG.Error("创建文件记录失败!", zap.Error(err)) + response.FailWithMessage("创建文件记录失败", c) + return + } + response.OkWithMessage("切片创建成功", c) +} + +// FindFile +// @Tags ExaFileUploadAndDownload +// @Summary 查找文件 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param file formData file true "Find the file, 查找文件" +// @Success 200 {object} response.Response{data=exampleRes.FileResponse,msg=string} "查找文件,返回包括文件详情" +// @Router /fileUploadAndDownload/findFile [get] +func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) { + fileMd5 := c.Query("fileMd5") + fileName := c.Query("fileName") + chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal")) + file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal) + if err != nil { + global.GVA_LOG.Error("查找失败!", zap.Error(err)) + response.FailWithMessage("查找失败", c) + } else { + response.OkWithDetailed(exampleRes.FileResponse{File: file}, "查找成功", c) + } +} + +// BreakpointContinueFinish +// @Tags ExaFileUploadAndDownload +// @Summary 创建文件 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param file formData file true "上传文件完成" +// @Success 200 {object} response.Response{data=exampleRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径" +// @Router /fileUploadAndDownload/findFile [post] +func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) { + fileMd5 := c.Query("fileMd5") + fileName := c.Query("fileName") + filePath, err := utils.MakeFile(fileName, fileMd5) + if err != nil { + global.GVA_LOG.Error("文件创建失败!", zap.Error(err)) + response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c) + } else { + response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c) + } +} + +// RemoveChunk +// @Tags ExaFileUploadAndDownload +// @Summary 删除切片 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param file formData file true "删除缓存切片" +// @Success 200 {object} response.Response{msg=string} "删除切片" +// @Router /fileUploadAndDownload/removeChunk [post] +func (b *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) { + var file example.ExaFile + err := c.ShouldBindJSON(&file) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.RemoveChunk(file.FileMd5) + if err != nil { + global.GVA_LOG.Error("缓存切片删除失败!", zap.Error(err)) + return + } + err = fileUploadAndDownloadService.DeleteFileChunk(file.FileMd5, file.FilePath) + if err != nil { + global.GVA_LOG.Error(err.Error(), zap.Error(err)) + response.FailWithMessage(err.Error(), c) + return + } + response.OkWithMessage("缓存切片删除成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_customer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_customer.go new file mode 100644 index 000000000..5d9ef1c02 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_customer.go @@ -0,0 +1,176 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type CustomerApi struct{} + +// CreateExaCustomer +// @Tags ExaCustomer +// @Summary 创建客户 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码" +// @Success 200 {object} response.Response{msg=string} "创建客户" +// @Router /customer/customer [post] +func (e *CustomerApi) CreateExaCustomer(c *gin.Context) { + var customer example.ExaCustomer + err := c.ShouldBindJSON(&customer) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(customer, utils.CustomerVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + customer.SysUserID = utils.GetUserID(c) + customer.SysUserAuthorityID = utils.GetUserAuthorityId(c) + err = customerService.CreateExaCustomer(customer) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// DeleteExaCustomer +// @Tags ExaCustomer +// @Summary 删除客户 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body example.ExaCustomer true "客户ID" +// @Success 200 {object} response.Response{msg=string} "删除客户" +// @Router /customer/customer [delete] +func (e *CustomerApi) DeleteExaCustomer(c *gin.Context) { + var customer example.ExaCustomer + err := c.ShouldBindJSON(&customer) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(customer.GVA_MODEL, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = customerService.DeleteExaCustomer(customer) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// UpdateExaCustomer +// @Tags ExaCustomer +// @Summary 更新客户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body example.ExaCustomer true "客户ID, 客户信息" +// @Success 200 {object} response.Response{msg=string} "更新客户信息" +// @Router /customer/customer [put] +func (e *CustomerApi) UpdateExaCustomer(c *gin.Context) { + var customer example.ExaCustomer + err := c.ShouldBindJSON(&customer) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(customer.GVA_MODEL, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(customer, utils.CustomerVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = customerService.UpdateExaCustomer(&customer) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + return + } + response.OkWithMessage("更新成功", c) +} + +// GetExaCustomer +// @Tags ExaCustomer +// @Summary 获取单一客户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query example.ExaCustomer true "客户ID" +// @Success 200 {object} response.Response{data=exampleRes.ExaCustomerResponse,msg=string} "获取单一客户信息,返回包括客户详情" +// @Router /customer/customer [get] +func (e *CustomerApi) GetExaCustomer(c *gin.Context) { + var customer example.ExaCustomer + err := c.ShouldBindQuery(&customer) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(customer.GVA_MODEL, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + data, err := customerService.GetExaCustomer(customer.ID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c) +} + +// GetExaCustomerList +// @Tags ExaCustomer +// @Summary 分页获取权限客户列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取权限客户列表,返回包括列表,总数,页码,每页数量" +// @Router /customer/customerList [get] +func (e *CustomerApi) GetExaCustomerList(c *gin.Context) { + var pageInfo request.PageInfo + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(pageInfo, utils.PageInfoVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + customerList, total, err := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败"+err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{ + List: customerList, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_file_upload_download.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_file_upload_download.go new file mode 100644 index 000000000..6905936d7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/example/exa_file_upload_download.go @@ -0,0 +1,133 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type FileUploadAndDownloadApi struct{} + +// UploadFile +// @Tags ExaFileUploadAndDownload +// @Summary 上传文件示例 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param file formData file true "上传文件示例" +// @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情" +// @Router /fileUploadAndDownload/upload [post] +func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) { + var file example.ExaFileUploadAndDownload + noSave := c.DefaultQuery("noSave", "0") + _, header, err := c.Request.FormFile("file") + if err != nil { + global.GVA_LOG.Error("接收文件失败!", zap.Error(err)) + response.FailWithMessage("接收文件失败", c) + return + } + file, err = fileUploadAndDownloadService.UploadFile(header, noSave) // 文件上传后拿到文件路径 + if err != nil { + global.GVA_LOG.Error("上传文件失败!", zap.Error(err)) + response.FailWithMessage("上传文件失败", c) + return + } + response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c) +} + +// EditFileName 编辑文件名或者备注 +func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) { + var file example.ExaFileUploadAndDownload + err := c.ShouldBindJSON(&file) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = fileUploadAndDownloadService.EditFileName(file) + if err != nil { + global.GVA_LOG.Error("编辑失败!", zap.Error(err)) + response.FailWithMessage("编辑失败", c) + return + } + response.OkWithMessage("编辑成功", c) +} + +// DeleteFile +// @Tags ExaFileUploadAndDownload +// @Summary 删除文件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可" +// @Success 200 {object} response.Response{msg=string} "删除文件" +// @Router /fileUploadAndDownload/deleteFile [post] +func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) { + var file example.ExaFileUploadAndDownload + err := c.ShouldBindJSON(&file) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := fileUploadAndDownloadService.DeleteFile(file); err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// GetFileList +// @Tags ExaFileUploadAndDownload +// @Summary 分页文件列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量" +// @Router /fileUploadAndDownload/getFileList [post] +func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) { + var pageInfo request.PageInfo + err := c.ShouldBindJSON(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := fileUploadAndDownloadService.GetFileRecordInfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +// ImportURL +// @Tags ExaFileUploadAndDownload +// @Summary 导入URL +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body example.ExaFileUploadAndDownload true "对象" +// @Success 200 {object} response.Response{msg=string} "导入URL" +// @Router /fileUploadAndDownload/importURL [post] +func (b *FileUploadAndDownloadApi) ImportURL(c *gin.Context) { + var file []example.ExaFileUploadAndDownload + err := c.ShouldBindJSON(&file) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := fileUploadAndDownloadService.ImportURL(&file); err != nil { + global.GVA_LOG.Error("导入URL失败!", zap.Error(err)) + response.FailWithMessage("导入URL失败", c) + return + } + response.OkWithMessage("导入URL成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_history.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_history.go new file mode 100644 index 000000000..065ddd86c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_history.go @@ -0,0 +1,115 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AutoCodeHistoryApi struct{} + +// First +// @Tags AutoCode +// @Summary 获取meta信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetById true "请求参数" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取meta信息" +// @Router /autoCode/getMeta [post] +func (a *AutoCodeHistoryApi) First(c *gin.Context) { + var info common.GetById + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + data, err := autoCodeHistoryService.First(c.Request.Context(), info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c) +} + +// Delete +// @Tags AutoCode +// @Summary 删除回滚记录 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetById true "请求参数" +// @Success 200 {object} response.Response{msg=string} "删除回滚记录" +// @Router /autoCode/delSysHistory [post] +func (a *AutoCodeHistoryApi) Delete(c *gin.Context) { + var info common.GetById + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = autoCodeHistoryService.Delete(c.Request.Context(), info) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// RollBack +// @Tags AutoCode +// @Summary 回滚自动生成代码 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.SysAutoHistoryRollBack true "请求参数" +// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码" +// @Router /autoCode/rollback [post] +func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) { + var info request.SysAutoHistoryRollBack + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = autoCodeHistoryService.RollBack(c.Request.Context(), info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + response.OkWithMessage("回滚成功", c) +} + +// GetList +// @Tags AutoCode +// @Summary 查询回滚记录 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body common.PageInfo true "请求参数" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量" +// @Router /autoCode/getSysHistory [post] +func (a *AutoCodeHistoryApi) GetList(c *gin.Context) { + var info common.PageInfo + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := autoCodeHistoryService.GetList(c.Request.Context(), info) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: info.Page, + PageSize: info.PageSize, + }, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_package.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_package.go new file mode 100644 index 000000000..655f29ab2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_package.go @@ -0,0 +1,100 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" + "strings" +) + +type AutoCodePackageApi struct{} + +// Create +// @Tags AutoCodePackage +// @Summary 创建package +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.SysAutoCodePackageCreate true "创建package" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功" +// @Router /autoCode/createPackage [post] +func (a *AutoCodePackageApi) Create(c *gin.Context) { + var info request.SysAutoCodePackageCreate + _ = c.ShouldBindJSON(&info) + if err := utils.Verify(info, utils.AutoPackageVerify); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if strings.Contains(info.PackageName, "\\") || strings.Contains(info.PackageName, "/") || strings.Contains(info.PackageName, "..") { + response.FailWithMessage("包名不合法", c) + return + } // PackageName可能导致路径穿越的问题 / 和 \ 都要防止 + err := autoCodePackageService.Create(c.Request.Context(), &info) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// Delete +// @Tags AutoCode +// @Summary 删除package +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body common.GetById true "创建package" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功" +// @Router /autoCode/delPackage [post] +func (a *AutoCodePackageApi) Delete(c *gin.Context) { + var info common.GetById + _ = c.ShouldBindJSON(&info) + err := autoCodePackageService.Delete(c.Request.Context(), info) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// All +// @Tags AutoCodePackage +// @Summary 获取package +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功" +// @Router /autoCode/getPackage [post] +func (a *AutoCodePackageApi) All(c *gin.Context) { + data, err := autoCodePackageService.All(c.Request.Context()) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(gin.H{"pkgs": data}, "获取成功", c) +} + +// Templates +// @Tags AutoCodePackage +// @Summary 获取package +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功" +// @Router /autoCode/getTemplates [get] +func (a *AutoCodePackageApi) Templates(c *gin.Context) { + data, err := autoCodePackageService.Templates(c.Request.Context()) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(data, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_plugin.go new file mode 100644 index 000000000..30029feb2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_plugin.go @@ -0,0 +1,119 @@ +package system + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AutoCodePluginApi struct{} + +// Install +// @Tags AutoCodePlugin +// @Summary 安装插件 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param plug formData file true "this is a test file" +// @Success 200 {object} response.Response{data=[]interface{},msg=string} "安装插件成功" +// @Router /autoCode/installPlugin [post] +func (a *AutoCodePluginApi) Install(c *gin.Context) { + header, err := c.FormFile("plug") + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + web, server, err := autoCodePluginService.Install(header) + webStr := "web插件安装成功" + serverStr := "server插件安装成功" + if web == -1 { + webStr = "web端插件未成功安装,请按照文档自行解压安装,如果为纯后端插件请忽略此条提示" + } + if server == -1 { + serverStr = "server端插件未成功安装,请按照文档自行解压安装,如果为纯前端插件请忽略此条提示" + } + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + response.OkWithData([]interface{}{ + gin.H{ + "code": web, + "msg": webStr, + }, + gin.H{ + "code": server, + "msg": serverStr, + }}, c) +} + +// Packaged +// @Tags AutoCodePlugin +// @Summary 打包插件 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param plugName query string true "插件名称" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功" +// @Router /autoCode/pubPlug [post] +func (a *AutoCodePluginApi) Packaged(c *gin.Context) { + plugName := c.Query("plugName") + zipPath, err := autoCodePluginService.PubPlug(plugName) + if err != nil { + global.GVA_LOG.Error("打包失败!", zap.Error(err)) + response.FailWithMessage("打包失败"+err.Error(), c) + return + } + response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c) +} + +// Packaged +// @Tags AutoCodePlugin +// @Summary 打包插件 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功" +// @Router /autoCode/initMenu [post] +func (a *AutoCodePluginApi) InitMenu(c *gin.Context) { + var menuInfo request.InitMenu + err := c.ShouldBindJSON(&menuInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = autoCodePluginService.InitMenu(menuInfo) + if err != nil { + global.GVA_LOG.Error("创建初始化Menu失败!", zap.Error(err)) + response.FailWithMessage("创建初始化Menu失败"+err.Error(), c) + return + } + response.OkWithMessage("文件变更成功", c) +} + +// Packaged +// @Tags AutoCodePlugin +// @Summary 打包插件 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功" +// @Router /autoCode/initAPI [post] +func (a *AutoCodePluginApi) InitAPI(c *gin.Context) { + var apiInfo request.InitApi + err := c.ShouldBindJSON(&apiInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = autoCodePluginService.InitAPI(apiInfo) + if err != nil { + global.GVA_LOG.Error("创建初始化API失败!", zap.Error(err)) + response.FailWithMessage("创建初始化API失败"+err.Error(), c) + return + } + response.OkWithMessage("文件变更成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_template.go new file mode 100644 index 000000000..18a5b9b5e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/auto_code_template.go @@ -0,0 +1,108 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AutoCodeTemplateApi struct{} + +// Preview +// @Tags AutoCodeTemplate +// @Summary 预览创建后的代码 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.AutoCode true "预览创建代码" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码" +// @Router /autoCode/preview [post] +func (a *AutoCodeTemplateApi) Preview(c *gin.Context) { + var info request.AutoCode + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(info, utils.AutoCodeVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = info.Pretreatment() + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + info.PackageT = utils.FirstUpper(info.Package) + autoCode, err := autoCodeTemplateService.Preview(c.Request.Context(), info) + if err != nil { + global.GVA_LOG.Error("预览失败!", zap.Error(err)) + response.FailWithMessage("预览失败", c) + } else { + response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c) + } +} + +// Create +// @Tags AutoCodeTemplate +// @Summary 自动代码模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.AutoCode true "创建自动代码" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /autoCode/createTemp [post] +func (a *AutoCodeTemplateApi) Create(c *gin.Context) { + var info request.AutoCode + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(info, utils.AutoCodeVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = info.Pretreatment() + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = autoCodeTemplateService.Create(c.Request.Context(), info) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage(err.Error(), c) + } else { + response.OkWithMessage("创建成功", c) + } +} + +// Create +// @Tags AddFunc +// @Summary 增加方法 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.AutoCode true "增加方法" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /autoCode/addFunc [post] +func (a *AutoCodeTemplateApi) AddFunc(c *gin.Context) { + var info request.AutoFunc + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = autoCodeTemplateService.AddFunc(info) + if err != nil { + global.GVA_LOG.Error("注入失败!", zap.Error(err)) + response.FailWithMessage("注入失败", c) + } else { + response.OkWithMessage("注入成功", c) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/enter.go new file mode 100644 index 000000000..c268ccc7b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/enter.go @@ -0,0 +1,47 @@ +package system + +import "github.com/flipped-aurora/gin-vue-admin/server/service" + +type ApiGroup struct { + DBApi + JwtApi + BaseApi + SystemApi + CasbinApi + AutoCodeApi + SystemApiApi + AuthorityApi + DictionaryApi + AuthorityMenuApi + OperationRecordApi + DictionaryDetailApi + AuthorityBtnApi + SysExportTemplateApi + AutoCodePluginApi + AutoCodePackageApi + AutoCodeHistoryApi + AutoCodeTemplateApi + SysParamsApi +} + +var ( + apiService = service.ServiceGroupApp.SystemServiceGroup.ApiService + jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService + menuService = service.ServiceGroupApp.SystemServiceGroup.MenuService + userService = service.ServiceGroupApp.SystemServiceGroup.UserService + initDBService = service.ServiceGroupApp.SystemServiceGroup.InitDBService + casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService + baseMenuService = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService + authorityService = service.ServiceGroupApp.SystemServiceGroup.AuthorityService + dictionaryService = service.ServiceGroupApp.SystemServiceGroup.DictionaryService + authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService + systemConfigService = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService + sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService + operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService + dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService + autoCodeService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService + autoCodePluginService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePlugin + autoCodePackageService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage + autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistory + autoCodeTemplateService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_api.go new file mode 100644 index 000000000..7c34f2cdb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_api.go @@ -0,0 +1,323 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type SystemApiApi struct{} + +// CreateApi +// @Tags SysApi +// @Summary 创建基础api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法" +// @Success 200 {object} response.Response{msg=string} "创建基础api" +// @Router /api/createApi [post] +func (s *SystemApiApi) CreateApi(c *gin.Context) { + var api system.SysApi + err := c.ShouldBindJSON(&api) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(api, utils.ApiVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = apiService.CreateApi(api) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// SyncApi +// @Tags SysApi +// @Summary 同步API +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "同步API" +// @Router /api/syncApi [get] +func (s *SystemApiApi) SyncApi(c *gin.Context) { + newApis, deleteApis, ignoreApis, err := apiService.SyncApi() + if err != nil { + global.GVA_LOG.Error("同步失败!", zap.Error(err)) + response.FailWithMessage("同步失败", c) + return + } + response.OkWithData(gin.H{ + "newApis": newApis, + "deleteApis": deleteApis, + "ignoreApis": ignoreApis, + }, c) +} + +// GetApiGroups +// @Tags SysApi +// @Summary 获取API分组 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "获取API分组" +// @Router /api/getApiGroups [get] +func (s *SystemApiApi) GetApiGroups(c *gin.Context) { + groups, apiGroupMap, err := apiService.GetApiGroups() + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithData(gin.H{ + "groups": groups, + "apiGroupMap": apiGroupMap, + }, c) +} + +// IgnoreApi +// @Tags IgnoreApi +// @Summary 忽略API +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "同步API" +// @Router /api/ignoreApi [post] +func (s *SystemApiApi) IgnoreApi(c *gin.Context) { + var ignoreApi system.SysIgnoreApi + err := c.ShouldBindJSON(&ignoreApi) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = apiService.IgnoreApi(ignoreApi) + if err != nil { + global.GVA_LOG.Error("忽略失败!", zap.Error(err)) + response.FailWithMessage("忽略失败", c) + return + } + response.Ok(c) +} + +// EnterSyncApi +// @Tags SysApi +// @Summary 确认同步API +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "确认同步API" +// @Router /api/enterSyncApi [post] +func (s *SystemApiApi) EnterSyncApi(c *gin.Context) { + var syncApi systemRes.SysSyncApis + err := c.ShouldBindJSON(&syncApi) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = apiService.EnterSyncApi(syncApi) + if err != nil { + global.GVA_LOG.Error("忽略失败!", zap.Error(err)) + response.FailWithMessage("忽略失败", c) + return + } + response.Ok(c) +} + +// DeleteApi +// @Tags SysApi +// @Summary 删除api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysApi true "ID" +// @Success 200 {object} response.Response{msg=string} "删除api" +// @Router /api/deleteApi [post] +func (s *SystemApiApi) DeleteApi(c *gin.Context) { + var api system.SysApi + err := c.ShouldBindJSON(&api) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(api.GVA_MODEL, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = apiService.DeleteApi(api) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// GetApiList +// @Tags SysApi +// @Summary 分页获取API列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body systemReq.SearchApiParams true "分页获取API列表" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取API列表,返回包括列表,总数,页码,每页数量" +// @Router /api/getApiList [post] +func (s *SystemApiApi) GetApiList(c *gin.Context) { + var pageInfo systemReq.SearchApiParams + err := c.ShouldBindJSON(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +// GetApiById +// @Tags SysApi +// @Summary 根据id获取api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetById true "根据id获取api" +// @Success 200 {object} response.Response{data=systemRes.SysAPIResponse} "根据id获取api,返回包括api详情" +// @Router /api/getApiById [post] +func (s *SystemApiApi) GetApiById(c *gin.Context) { + var idInfo request.GetById + err := c.ShouldBindJSON(&idInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(idInfo, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + api, err := apiService.GetApiById(idInfo.ID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(systemRes.SysAPIResponse{Api: api}, "获取成功", c) +} + +// UpdateApi +// @Tags SysApi +// @Summary 修改基础api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法" +// @Success 200 {object} response.Response{msg=string} "修改基础api" +// @Router /api/updateApi [post] +func (s *SystemApiApi) UpdateApi(c *gin.Context) { + var api system.SysApi + err := c.ShouldBindJSON(&api) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(api, utils.ApiVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = apiService.UpdateApi(api) + if err != nil { + global.GVA_LOG.Error("修改失败!", zap.Error(err)) + response.FailWithMessage("修改失败", c) + return + } + response.OkWithMessage("修改成功", c) +} + +// GetAllApis +// @Tags SysApi +// @Summary 获取所有的Api 不分页 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=systemRes.SysAPIListResponse,msg=string} "获取所有的Api 不分页,返回包括api列表" +// @Router /api/getAllApis [post] +func (s *SystemApiApi) GetAllApis(c *gin.Context) { + authorityID := utils.GetUserAuthorityId(c) + apis, err := apiService.GetAllApis(authorityID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c) +} + +// DeleteApisByIds +// @Tags SysApi +// @Summary 删除选中Api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "ID" +// @Success 200 {object} response.Response{msg=string} "删除选中Api" +// @Router /api/deleteApisByIds [delete] +func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) { + var ids request.IdsReq + err := c.ShouldBindJSON(&ids) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = apiService.DeleteApisByIds(ids) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// FreshCasbin +// @Tags SysApi +// @Summary 刷新casbin缓存 +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "刷新成功" +// @Router /api/freshCasbin [get] +func (s *SystemApiApi) FreshCasbin(c *gin.Context) { + err := casbinService.FreshCasbin() + if err != nil { + global.GVA_LOG.Error("刷新失败!", zap.Error(err)) + response.FailWithMessage("刷新失败", c) + return + } + response.OkWithMessage("刷新成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_authority.go new file mode 100644 index 000000000..b34fc3a0b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_authority.go @@ -0,0 +1,202 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AuthorityApi struct{} + +// CreateAuthority +// @Tags Authority +// @Summary 创建角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id" +// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "创建角色,返回包括系统角色详情" +// @Router /authority/createAuthority [post] +func (a *AuthorityApi) CreateAuthority(c *gin.Context) { + var authority, authBack system.SysAuthority + var err error + + if err = c.ShouldBindJSON(&authority); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + if err = utils.Verify(authority, utils.AuthorityVerify); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + if *authority.ParentId == 0 && global.GVA_CONFIG.System.UseStrictAuth { + authority.ParentId = utils.Pointer(utils.GetUserAuthorityId(c)) + } + + if authBack, err = authorityService.CreateAuthority(authority); err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败"+err.Error(), c) + return + } + err = casbinService.FreshCasbin() + if err != nil { + global.GVA_LOG.Error("创建成功,权限刷新失败。", zap.Error(err)) + response.FailWithMessage("创建成功,权限刷新失败。"+err.Error(), c) + return + } + response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c) +} + +// CopyAuthority +// @Tags Authority +// @Summary 拷贝角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body response.SysAuthorityCopyResponse true "旧角色id, 新权限id, 新权限名, 新父角色id" +// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "拷贝角色,返回包括系统角色详情" +// @Router /authority/copyAuthority [post] +func (a *AuthorityApi) CopyAuthority(c *gin.Context) { + var copyInfo systemRes.SysAuthorityCopyResponse + err := c.ShouldBindJSON(©Info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(copyInfo, utils.OldAuthorityVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(copyInfo.Authority, utils.AuthorityVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + adminAuthorityID := utils.GetUserAuthorityId(c) + authBack, err := authorityService.CopyAuthority(adminAuthorityID, copyInfo) + if err != nil { + global.GVA_LOG.Error("拷贝失败!", zap.Error(err)) + response.FailWithMessage("拷贝失败"+err.Error(), c) + return + } + response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "拷贝成功", c) +} + +// DeleteAuthority +// @Tags Authority +// @Summary 删除角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysAuthority true "删除角色" +// @Success 200 {object} response.Response{msg=string} "删除角色" +// @Router /authority/deleteAuthority [post] +func (a *AuthorityApi) DeleteAuthority(c *gin.Context) { + var authority system.SysAuthority + var err error + if err = c.ShouldBindJSON(&authority); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err = utils.Verify(authority, utils.AuthorityIdVerify); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + // 删除角色之前需要判断是否有用户正在使用此角色 + if err = authorityService.DeleteAuthority(&authority); err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败"+err.Error(), c) + return + } + _ = casbinService.FreshCasbin() + response.OkWithMessage("删除成功", c) +} + +// UpdateAuthority +// @Tags Authority +// @Summary 更新角色信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id" +// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "更新角色信息,返回包括系统角色详情" +// @Router /authority/updateAuthority [put] +func (a *AuthorityApi) UpdateAuthority(c *gin.Context) { + var auth system.SysAuthority + err := c.ShouldBindJSON(&auth) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(auth, utils.AuthorityVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + authority, err := authorityService.UpdateAuthority(auth) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败"+err.Error(), c) + return + } + response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c) +} + +// GetAuthorityList +// @Tags Authority +// @Summary 分页获取角色列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取角色列表,返回包括列表,总数,页码,每页数量" +// @Router /authority/getAuthorityList [post] +func (a *AuthorityApi) GetAuthorityList(c *gin.Context) { + authorityID := utils.GetUserAuthorityId(c) + list, err := authorityService.GetAuthorityInfoList(authorityID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败"+err.Error(), c) + return + } + response.OkWithDetailed(list, "获取成功", c) +} + +// SetDataAuthority +// @Tags Authority +// @Summary 设置角色资源权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysAuthority true "设置角色资源权限" +// @Success 200 {object} response.Response{msg=string} "设置角色资源权限" +// @Router /authority/setDataAuthority [post] +func (a *AuthorityApi) SetDataAuthority(c *gin.Context) { + var auth system.SysAuthority + err := c.ShouldBindJSON(&auth) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(auth, utils.AuthorityIdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + adminAuthorityID := utils.GetUserAuthorityId(c) + err = authorityService.SetDataAuthority(adminAuthorityID, auth) + if err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败"+err.Error(), c) + return + } + response.OkWithMessage("设置成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_authority_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_authority_btn.go new file mode 100644 index 000000000..94f02a00e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_authority_btn.go @@ -0,0 +1,80 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AuthorityBtnApi struct{} + +// GetAuthorityBtn +// @Tags AuthorityBtn +// @Summary 获取权限按钮 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id" +// @Success 200 {object} response.Response{data=response.SysAuthorityBtnRes,msg=string} "返回列表成功" +// @Router /authorityBtn/getAuthorityBtn [post] +func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) { + var req request.SysAuthorityBtnReq + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + res, err := authorityBtnService.GetAuthorityBtn(req) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + return + } + response.OkWithDetailed(res, "查询成功", c) +} + +// SetAuthorityBtn +// @Tags AuthorityBtn +// @Summary 设置权限按钮 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id" +// @Success 200 {object} response.Response{msg=string} "返回列表成功" +// @Router /authorityBtn/setAuthorityBtn [post] +func (a *AuthorityBtnApi) SetAuthorityBtn(c *gin.Context) { + var req request.SysAuthorityBtnReq + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = authorityBtnService.SetAuthorityBtn(req) + if err != nil { + global.GVA_LOG.Error("分配失败!", zap.Error(err)) + response.FailWithMessage("分配失败", c) + return + } + response.OkWithMessage("分配成功", c) +} + +// CanRemoveAuthorityBtn +// @Tags AuthorityBtn +// @Summary 设置权限按钮 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "删除成功" +// @Router /authorityBtn/canRemoveAuthorityBtn [post] +func (a *AuthorityBtnApi) CanRemoveAuthorityBtn(c *gin.Context) { + id := c.Query("id") + err := authorityBtnService.CanRemoveAuthorityBtn(id) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage(err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_auto_code.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_auto_code.go new file mode 100644 index 000000000..02f458bb3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_auto_code.go @@ -0,0 +1,155 @@ +package system + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/model/common" + "github.com/goccy/go-json" + "io" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AutoCodeApi struct{} + +// GetDB +// @Tags AutoCode +// @Summary 获取当前所有数据库 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前所有数据库" +// @Router /autoCode/getDB [get] +func (autoApi *AutoCodeApi) GetDB(c *gin.Context) { + businessDB := c.Query("businessDB") + dbs, err := autoCodeService.Database(businessDB).GetDB(businessDB) + var dbList []map[string]interface{} + for _, db := range global.GVA_CONFIG.DBList { + var item = make(map[string]interface{}) + item["aliasName"] = db.AliasName + item["dbName"] = db.Dbname + item["disable"] = db.Disable + item["dbtype"] = db.Type + dbList = append(dbList, item) + } + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithDetailed(gin.H{"dbs": dbs, "dbList": dbList}, "获取成功", c) + } +} + +// GetTables +// @Tags AutoCode +// @Summary 获取当前数据库所有表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前数据库所有表" +// @Router /autoCode/getTables [get] +func (autoApi *AutoCodeApi) GetTables(c *gin.Context) { + dbName := c.Query("dbName") + businessDB := c.Query("businessDB") + if dbName == "" { + dbName = *global.GVA_ACTIVE_DBNAME + if businessDB != "" { + for _, db := range global.GVA_CONFIG.DBList { + if db.AliasName == businessDB { + dbName = db.Dbname + } + } + } + } + + tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName) + if err != nil { + global.GVA_LOG.Error("查询table失败!", zap.Error(err)) + response.FailWithMessage("查询table失败", c) + } else { + response.OkWithDetailed(gin.H{"tables": tables}, "获取成功", c) + } +} + +// GetColumn +// @Tags AutoCode +// @Summary 获取当前表所有字段 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前表所有字段" +// @Router /autoCode/getColumn [get] +func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) { + businessDB := c.Query("businessDB") + dbName := c.Query("dbName") + if dbName == "" { + dbName = *global.GVA_ACTIVE_DBNAME + if businessDB != "" { + for _, db := range global.GVA_CONFIG.DBList { + if db.AliasName == businessDB { + dbName = db.Dbname + } + } + } + } + tableName := c.Query("tableName") + columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c) + } +} + +func (autoApi *AutoCodeApi) LLMAuto(c *gin.Context) { + var llm common.JSONMap + err := c.ShouldBindJSON(&llm) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if global.GVA_CONFIG.AutoCode.AiPath == "" { + response.FailWithMessage("请先前往插件市场个人中心获取AiPath并填入config.yaml中", c) + return + } + + path := strings.ReplaceAll(global.GVA_CONFIG.AutoCode.AiPath, "{FUNC}", fmt.Sprintf("api/chat/%s", llm["mode"])) + res, err := request.HttpRequest( + path, + "POST", + nil, + nil, + llm, + ) + if err != nil { + global.GVA_LOG.Error("大模型生成失败!", zap.Error(err)) + response.FailWithMessage("大模型生成失败"+err.Error(), c) + return + } + var resStruct response.Response + b, err := io.ReadAll(res.Body) + defer res.Body.Close() + if err != nil { + global.GVA_LOG.Error("大模型生成失败!", zap.Error(err)) + response.FailWithMessage("大模型生成失败"+err.Error(), c) + return + } + err = json.Unmarshal(b, &resStruct) + if err != nil { + global.GVA_LOG.Error("大模型生成失败!", zap.Error(err)) + response.FailWithMessage("大模型生成失败"+err.Error(), c) + return + } + + if resStruct.Code == 7 { + global.GVA_LOG.Error("大模型生成失败!"+resStruct.Msg, zap.Error(err)) + response.FailWithMessage("大模型生成失败"+resStruct.Msg, c) + return + } + response.OkWithData(resStruct.Data, c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_captcha.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_captcha.go new file mode 100644 index 000000000..b9f2110ec --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_captcha.go @@ -0,0 +1,70 @@ +package system + +import ( + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/gin-gonic/gin" + "github.com/mojocn/base64Captcha" + "go.uber.org/zap" +) + +// 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码 +// var store = captcha.NewDefaultRedisStore() +var store = base64Captcha.DefaultMemStore + +type BaseApi struct{} + +// Captcha +// @Tags Base +// @Summary 生成验证码 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码" +// @Router /base/captcha [post] +func (b *BaseApi) Captcha(c *gin.Context) { + // 判断验证码是否开启 + openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数 + openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间 + key := c.ClientIP() + v, ok := global.BlackCache.Get(key) + if !ok { + global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut)) + } + + var oc bool + if openCaptcha == 0 || openCaptcha < interfaceToInt(v) { + oc = true + } + // 字符,公式,验证码配置 + // 生成默认数字的driver + driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80) + // cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c)) // v8下使用redis + cp := base64Captcha.NewCaptcha(driver, store) + id, b64s, _, err := cp.Generate() + if err != nil { + global.GVA_LOG.Error("验证码获取失败!", zap.Error(err)) + response.FailWithMessage("验证码获取失败", c) + return + } + response.OkWithDetailed(systemRes.SysCaptchaResponse{ + CaptchaId: id, + PicPath: b64s, + CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong, + OpenCaptcha: oc, + }, "验证码获取成功", c) +} + +// 类型转换 +func interfaceToInt(v interface{}) (i int) { + switch v := v.(type) { + case int: + i = v + default: + i = 0 + } + return +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_casbin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_casbin.go new file mode 100644 index 000000000..c1bf54894 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_casbin.go @@ -0,0 +1,69 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type CasbinApi struct{} + +// UpdateCasbin +// @Tags Casbin +// @Summary 更新角色api权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.CasbinInReceive true "权限id, 权限模型列表" +// @Success 200 {object} response.Response{msg=string} "更新角色api权限" +// @Router /casbin/UpdateCasbin [post] +func (cas *CasbinApi) UpdateCasbin(c *gin.Context) { + var cmr request.CasbinInReceive + err := c.ShouldBindJSON(&cmr) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(cmr, utils.AuthorityIdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + adminAuthorityID := utils.GetUserAuthorityId(c) + err = casbinService.UpdateCasbin(adminAuthorityID, cmr.AuthorityId, cmr.CasbinInfos) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + return + } + response.OkWithMessage("更新成功", c) +} + +// GetPolicyPathByAuthorityId +// @Tags Casbin +// @Summary 获取权限列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.CasbinInReceive true "权限id, 权限模型列表" +// @Success 200 {object} response.Response{data=systemRes.PolicyPathResponse,msg=string} "获取权限列表,返回包括casbin详情列表" +// @Router /casbin/getPolicyPathByAuthorityId [post] +func (cas *CasbinApi) GetPolicyPathByAuthorityId(c *gin.Context) { + var casbin request.CasbinInReceive + err := c.ShouldBindJSON(&casbin) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(casbin, utils.AuthorityIdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + paths := casbinService.GetPolicyPathByAuthorityId(casbin.AuthorityId) + response.OkWithDetailed(systemRes.PolicyPathResponse{Paths: paths}, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_dictionary.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_dictionary.go new file mode 100644 index 000000000..1dfe9d089 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_dictionary.go @@ -0,0 +1,129 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type DictionaryApi struct{} + +// CreateSysDictionary +// @Tags SysDictionary +// @Summary 创建SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysDictionary true "SysDictionary模型" +// @Success 200 {object} response.Response{msg=string} "创建SysDictionary" +// @Router /sysDictionary/createSysDictionary [post] +func (s *DictionaryApi) CreateSysDictionary(c *gin.Context) { + var dictionary system.SysDictionary + err := c.ShouldBindJSON(&dictionary) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = dictionaryService.CreateSysDictionary(dictionary) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// DeleteSysDictionary +// @Tags SysDictionary +// @Summary 删除SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysDictionary true "SysDictionary模型" +// @Success 200 {object} response.Response{msg=string} "删除SysDictionary" +// @Router /sysDictionary/deleteSysDictionary [delete] +func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) { + var dictionary system.SysDictionary + err := c.ShouldBindJSON(&dictionary) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = dictionaryService.DeleteSysDictionary(dictionary) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// UpdateSysDictionary +// @Tags SysDictionary +// @Summary 更新SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysDictionary true "SysDictionary模型" +// @Success 200 {object} response.Response{msg=string} "更新SysDictionary" +// @Router /sysDictionary/updateSysDictionary [put] +func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) { + var dictionary system.SysDictionary + err := c.ShouldBindJSON(&dictionary) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = dictionaryService.UpdateSysDictionary(&dictionary) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + return + } + response.OkWithMessage("更新成功", c) +} + +// FindSysDictionary +// @Tags SysDictionary +// @Summary 用id查询SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query system.SysDictionary true "ID或字典英名" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionary" +// @Router /sysDictionary/findSysDictionary [get] +func (s *DictionaryApi) FindSysDictionary(c *gin.Context) { + var dictionary system.SysDictionary + err := c.ShouldBindQuery(&dictionary) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + sysDictionary, err := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID, dictionary.Status) + if err != nil { + global.GVA_LOG.Error("字典未创建或未开启!", zap.Error(err)) + response.FailWithMessage("字典未创建或未开启", c) + return + } + response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c) +} + +// GetSysDictionaryList +// @Tags SysDictionary +// @Summary 分页获取SysDictionary列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量" +// @Router /sysDictionary/getSysDictionaryList [get] +func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) { + list, err := dictionaryService.GetSysDictionaryInfoList() + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(list, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_dictionary_detail.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_dictionary_detail.go new file mode 100644 index 000000000..754af1be6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_dictionary_detail.go @@ -0,0 +1,148 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type DictionaryDetailApi struct{} + +// CreateSysDictionaryDetail +// @Tags SysDictionaryDetail +// @Summary 创建SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型" +// @Success 200 {object} response.Response{msg=string} "创建SysDictionaryDetail" +// @Router /sysDictionaryDetail/createSysDictionaryDetail [post] +func (s *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) { + var detail system.SysDictionaryDetail + err := c.ShouldBindJSON(&detail) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = dictionaryDetailService.CreateSysDictionaryDetail(detail) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// DeleteSysDictionaryDetail +// @Tags SysDictionaryDetail +// @Summary 删除SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型" +// @Success 200 {object} response.Response{msg=string} "删除SysDictionaryDetail" +// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete] +func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) { + var detail system.SysDictionaryDetail + err := c.ShouldBindJSON(&detail) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = dictionaryDetailService.DeleteSysDictionaryDetail(detail) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// UpdateSysDictionaryDetail +// @Tags SysDictionaryDetail +// @Summary 更新SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysDictionaryDetail true "更新SysDictionaryDetail" +// @Success 200 {object} response.Response{msg=string} "更新SysDictionaryDetail" +// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put] +func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) { + var detail system.SysDictionaryDetail + err := c.ShouldBindJSON(&detail) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = dictionaryDetailService.UpdateSysDictionaryDetail(&detail) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + return + } + response.OkWithMessage("更新成功", c) +} + +// FindSysDictionaryDetail +// @Tags SysDictionaryDetail +// @Summary 用id查询SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query system.SysDictionaryDetail true "用id查询SysDictionaryDetail" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionaryDetail" +// @Router /sysDictionaryDetail/findSysDictionaryDetail [get] +func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) { + var detail system.SysDictionaryDetail + err := c.ShouldBindQuery(&detail) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(detail, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + reSysDictionaryDetail, err := dictionaryDetailService.GetSysDictionaryDetail(detail.ID) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + return + } + response.OkWithDetailed(gin.H{"reSysDictionaryDetail": reSysDictionaryDetail}, "查询成功", c) +} + +// GetSysDictionaryDetailList +// @Tags SysDictionaryDetail +// @Summary 分页获取SysDictionaryDetail列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.SysDictionaryDetailSearch true "页码, 每页大小, 搜索条件" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量" +// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get] +func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) { + var pageInfo request.SysDictionaryDetailSearch + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_export_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_export_template.go new file mode 100644 index 000000000..38b22965b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_export_template.go @@ -0,0 +1,258 @@ +package system + +import ( + "fmt" + "net/http" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/service" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type SysExportTemplateApi struct { +} + +var sysExportTemplateService = service.ServiceGroupApp.SystemServiceGroup.SysExportTemplateService + +// CreateSysExportTemplate 创建导出模板 +// @Tags SysExportTemplate +// @Summary 创建导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysExportTemplate true "创建导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /sysExportTemplate/createSysExportTemplate [post] +func (sysExportTemplateApi *SysExportTemplateApi) CreateSysExportTemplate(c *gin.Context) { + var sysExportTemplate system.SysExportTemplate + err := c.ShouldBindJSON(&sysExportTemplate) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + verify := utils.Rules{ + "Name": {utils.NotEmpty()}, + } + if err := utils.Verify(sysExportTemplate, verify); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := sysExportTemplateService.CreateSysExportTemplate(&sysExportTemplate); err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + } else { + response.OkWithMessage("创建成功", c) + } +} + +// DeleteSysExportTemplate 删除导出模板 +// @Tags SysExportTemplate +// @Summary 删除导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysExportTemplate true "删除导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysExportTemplate/deleteSysExportTemplate [delete] +func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) { + var sysExportTemplate system.SysExportTemplate + err := c.ShouldBindJSON(&sysExportTemplate) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := sysExportTemplateService.DeleteSysExportTemplate(sysExportTemplate); err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + } else { + response.OkWithMessage("删除成功", c) + } +} + +// DeleteSysExportTemplateByIds 批量删除导出模板 +// @Tags SysExportTemplate +// @Summary 批量删除导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}" +// @Router /sysExportTemplate/deleteSysExportTemplateByIds [delete] +func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) { + var IDS request.IdsReq + err := c.ShouldBindJSON(&IDS) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := sysExportTemplateService.DeleteSysExportTemplateByIds(IDS); err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败", c) + } else { + response.OkWithMessage("批量删除成功", c) + } +} + +// UpdateSysExportTemplate 更新导出模板 +// @Tags SysExportTemplate +// @Summary 更新导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysExportTemplate true "更新导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /sysExportTemplate/updateSysExportTemplate [put] +func (sysExportTemplateApi *SysExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) { + var sysExportTemplate system.SysExportTemplate + err := c.ShouldBindJSON(&sysExportTemplate) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + verify := utils.Rules{ + "Name": {utils.NotEmpty()}, + } + if err := utils.Verify(sysExportTemplate, verify); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := sysExportTemplateService.UpdateSysExportTemplate(sysExportTemplate); err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + } else { + response.OkWithMessage("更新成功", c) + } +} + +// FindSysExportTemplate 用id查询导出模板 +// @Tags SysExportTemplate +// @Summary 用id查询导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query system.SysExportTemplate true "用id查询导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /sysExportTemplate/findSysExportTemplate [get] +func (sysExportTemplateApi *SysExportTemplateApi) FindSysExportTemplate(c *gin.Context) { + var sysExportTemplate system.SysExportTemplate + err := c.ShouldBindQuery(&sysExportTemplate) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if resysExportTemplate, err := sysExportTemplateService.GetSysExportTemplate(sysExportTemplate.ID); err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + } else { + response.OkWithData(gin.H{"resysExportTemplate": resysExportTemplate}, c) + } +} + +// GetSysExportTemplateList 分页获取导出模板列表 +// @Tags SysExportTemplate +// @Summary 分页获取导出模板列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query systemReq.SysExportTemplateSearch true "分页获取导出模板列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysExportTemplate/getSysExportTemplateList [get] +func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gin.Context) { + var pageInfo systemReq.SysExportTemplateSearch + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if list, total, err := sysExportTemplateService.GetSysExportTemplateInfoList(pageInfo); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) + } +} + +// ExportExcel 导出表格 +// @Tags SysExportTemplate +// @Summary 导出表格 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Router /sysExportTemplate/exportExcel [get] +func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) { + templateID := c.Query("templateID") + queryParams := c.Request.URL.Query() + if templateID == "" { + response.FailWithMessage("模板ID不能为空", c) + return + } + if file, name, err := sysExportTemplateService.ExportExcel(templateID, queryParams); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx")) // 对下载的文件重命名 + c.Header("success", "true") + c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes()) + } +} + +// ExportTemplate 导出表格模板 +// @Tags SysExportTemplate +// @Summary 导出表格模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Router /sysExportTemplate/exportExcel [get] +func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplate(c *gin.Context) { + templateID := c.Query("templateID") + if templateID == "" { + response.FailWithMessage("模板ID不能为空", c) + return + } + if file, name, err := sysExportTemplateService.ExportTemplate(templateID); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx")) // 对下载的文件重命名 + c.Header("success", "true") + c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes()) + } +} + +// ImportExcel 导入表格 +// @Tags SysImportTemplate +// @Summary 导入表格 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Router /sysExportTemplate/importExcel [post] +func (sysExportTemplateApi *SysExportTemplateApi) ImportExcel(c *gin.Context) { + templateID := c.Query("templateID") + if templateID == "" { + response.FailWithMessage("模板ID不能为空", c) + return + } + file, err := c.FormFile("file") + if err != nil { + global.GVA_LOG.Error("文件获取失败!", zap.Error(err)) + response.FailWithMessage("文件获取失败", c) + return + } + if err := sysExportTemplateService.ImportExcel(templateID, file); err != nil { + global.GVA_LOG.Error(err.Error(), zap.Error(err)) + response.FailWithMessage(err.Error(), c) + } else { + response.OkWithMessage("导入成功", c) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_initdb.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_initdb.go new file mode 100644 index 000000000..684de2680 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_initdb.go @@ -0,0 +1,64 @@ +package system + +import ( + "log" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "go.uber.org/zap" + + "github.com/gin-gonic/gin" +) + +type DBApi struct{} + +// InitDB +// @Tags InitDB +// @Summary 初始化用户数据库 +// @Produce application/json +// @Param data body request.InitDB true "初始化数据库参数" +// @Success 200 {object} response.Response{data=string} "初始化用户数据库" +// @Router /init/initdb [post] +func (i *DBApi) InitDB(c *gin.Context) { + if global.GVA_DB != nil { + global.GVA_LOG.Error("已存在数据库配置!") + response.FailWithMessage("已存在数据库配置", c) + return + } + var dbInfo request.InitDB + dbInfo.AdminPassword = "123456" + log.Println("dbInfo:", dbInfo) + if err := c.ShouldBindJSON(&dbInfo); err != nil { + global.GVA_LOG.Error("参数校验不通过!", zap.Error(err)) + response.FailWithMessage("参数校验不通过", c) + return + } + if err := initDBService.InitDB(dbInfo); err != nil { + log.Println("err:", err) + global.GVA_LOG.Error("自动创建数据库失败!", zap.Error(err)) + response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c) + return + } + response.OkWithMessage("自动创建数据库成功", c) +} + +// CheckDB +// @Tags CheckDB +// @Summary 初始化用户数据库 +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "初始化用户数据库" +// @Router /init/checkdb [post] +func (i *DBApi) CheckDB(c *gin.Context) { + var ( + message = "前往初始化数据库" + needInit = true + ) + + if global.GVA_DB != nil { + message = "数据库无需初始化" + needInit = false + } + global.GVA_LOG.Info(message) + response.OkWithDetailed(gin.H{"needInit": needInit}, message, c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_jwt_blacklist.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_jwt_blacklist.go new file mode 100644 index 000000000..f66c155c7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_jwt_blacklist.go @@ -0,0 +1,33 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type JwtApi struct{} + +// JsonInBlacklist +// @Tags Jwt +// @Summary jwt加入黑名单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "jwt加入黑名单" +// @Router /jwt/jsonInBlacklist [post] +func (j *JwtApi) JsonInBlacklist(c *gin.Context) { + token := utils.GetToken(c) + jwt := system.JwtBlacklist{Jwt: token} + err := jwtService.JsonInBlacklist(jwt) + if err != nil { + global.GVA_LOG.Error("jwt作废失败!", zap.Error(err)) + response.FailWithMessage("jwt作废失败", c) + return + } + utils.ClearToken(c) + response.OkWithMessage("jwt作废成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_menu.go new file mode 100644 index 000000000..864b61f38 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_menu.go @@ -0,0 +1,265 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type AuthorityMenuApi struct{} + +// GetMenu +// @Tags AuthorityMenu +// @Summary 获取用户动态路由 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body request.Empty true "空" +// @Success 200 {object} response.Response{data=systemRes.SysMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单详情列表" +// @Router /menu/getMenu [post] +func (a *AuthorityMenuApi) GetMenu(c *gin.Context) { + menus, err := menuService.GetMenuTree(utils.GetUserAuthorityId(c)) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + if menus == nil { + menus = []system.SysMenu{} + } + response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c) +} + +// GetBaseMenuTree +// @Tags AuthorityMenu +// @Summary 获取用户动态路由 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body request.Empty true "空" +// @Success 200 {object} response.Response{data=systemRes.SysBaseMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单列表" +// @Router /menu/getBaseMenuTree [post] +func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) { + authority := utils.GetUserAuthorityId(c) + menus, err := menuService.GetBaseMenuTree(authority) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c) +} + +// AddMenuAuthority +// @Tags AuthorityMenu +// @Summary 增加menu和角色关联关系 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body systemReq.AddMenuAuthorityInfo true "角色ID" +// @Success 200 {object} response.Response{msg=string} "增加menu和角色关联关系" +// @Router /menu/addMenuAuthority [post] +func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) { + var authorityMenu systemReq.AddMenuAuthorityInfo + err := c.ShouldBindJSON(&authorityMenu) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := utils.Verify(authorityMenu, utils.AuthorityIdVerify); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + adminAuthorityID := utils.GetUserAuthorityId(c) + if err := menuService.AddMenuAuthority(authorityMenu.Menus, adminAuthorityID, authorityMenu.AuthorityId); err != nil { + global.GVA_LOG.Error("添加失败!", zap.Error(err)) + response.FailWithMessage("添加失败", c) + } else { + response.OkWithMessage("添加成功", c) + } +} + +// GetMenuAuthority +// @Tags AuthorityMenu +// @Summary 获取指定角色menu +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetAuthorityId true "角色ID" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取指定角色menu" +// @Router /menu/getMenuAuthority [post] +func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) { + var param request.GetAuthorityId + err := c.ShouldBindJSON(¶m) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(param, utils.AuthorityIdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + menus, err := menuService.GetMenuAuthority(¶m) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取失败", c) + return + } + response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c) +} + +// AddBaseMenu +// @Tags Menu +// @Summary 新增菜单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记" +// @Success 200 {object} response.Response{msg=string} "新增菜单" +// @Router /menu/addBaseMenu [post] +func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) { + var menu system.SysBaseMenu + err := c.ShouldBindJSON(&menu) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(menu, utils.MenuVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(menu.Meta, utils.MenuMetaVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = menuService.AddBaseMenu(menu) + if err != nil { + global.GVA_LOG.Error("添加失败!", zap.Error(err)) + response.FailWithMessage("添加失败", c) + return + } + response.OkWithMessage("添加成功", c) +} + +// DeleteBaseMenu +// @Tags Menu +// @Summary 删除菜单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetById true "菜单id" +// @Success 200 {object} response.Response{msg=string} "删除菜单" +// @Router /menu/deleteBaseMenu [post] +func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) { + var menu request.GetById + err := c.ShouldBindJSON(&menu) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(menu, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = baseMenuService.DeleteBaseMenu(menu.ID) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// UpdateBaseMenu +// @Tags Menu +// @Summary 更新菜单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记" +// @Success 200 {object} response.Response{msg=string} "更新菜单" +// @Router /menu/updateBaseMenu [post] +func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) { + var menu system.SysBaseMenu + err := c.ShouldBindJSON(&menu) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(menu, utils.MenuVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(menu.Meta, utils.MenuMetaVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = baseMenuService.UpdateBaseMenu(menu) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + return + } + response.OkWithMessage("更新成功", c) +} + +// GetBaseMenuById +// @Tags Menu +// @Summary 根据id获取菜单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetById true "菜单id" +// @Success 200 {object} response.Response{data=systemRes.SysBaseMenuResponse,msg=string} "根据id获取菜单,返回包括系统菜单列表" +// @Router /menu/getBaseMenuById [post] +func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) { + var idInfo request.GetById + err := c.ShouldBindJSON(&idInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(idInfo, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + menu, err := baseMenuService.GetBaseMenuById(idInfo.ID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c) +} + +// GetMenuList +// @Tags Menu +// @Summary 分页获取基础menu列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取基础menu列表,返回包括列表,总数,页码,每页数量" +// @Router /menu/getMenuList [post] +func (a *AuthorityMenuApi) GetMenuList(c *gin.Context) { + authorityID := utils.GetUserAuthorityId(c) + menuList, err := menuService.GetInfoList(authorityID) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(menuList, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_operation_record.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_operation_record.go new file mode 100644 index 000000000..40daeb98d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_operation_record.go @@ -0,0 +1,149 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type OperationRecordApi struct{} + +// CreateSysOperationRecord +// @Tags SysOperationRecord +// @Summary 创建SysOperationRecord +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysOperationRecord true "创建SysOperationRecord" +// @Success 200 {object} response.Response{msg=string} "创建SysOperationRecord" +// @Router /sysOperationRecord/createSysOperationRecord [post] +func (s *OperationRecordApi) CreateSysOperationRecord(c *gin.Context) { + var sysOperationRecord system.SysOperationRecord + err := c.ShouldBindJSON(&sysOperationRecord) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = operationRecordService.CreateSysOperationRecord(sysOperationRecord) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// DeleteSysOperationRecord +// @Tags SysOperationRecord +// @Summary 删除SysOperationRecord +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysOperationRecord true "SysOperationRecord模型" +// @Success 200 {object} response.Response{msg=string} "删除SysOperationRecord" +// @Router /sysOperationRecord/deleteSysOperationRecord [delete] +func (s *OperationRecordApi) DeleteSysOperationRecord(c *gin.Context) { + var sysOperationRecord system.SysOperationRecord + err := c.ShouldBindJSON(&sysOperationRecord) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = operationRecordService.DeleteSysOperationRecord(sysOperationRecord) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// DeleteSysOperationRecordByIds +// @Tags SysOperationRecord +// @Summary 批量删除SysOperationRecord +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除SysOperationRecord" +// @Success 200 {object} response.Response{msg=string} "批量删除SysOperationRecord" +// @Router /sysOperationRecord/deleteSysOperationRecordByIds [delete] +func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) { + var IDS request.IdsReq + err := c.ShouldBindJSON(&IDS) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = operationRecordService.DeleteSysOperationRecordByIds(IDS) + if err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败", c) + return + } + response.OkWithMessage("批量删除成功", c) +} + +// FindSysOperationRecord +// @Tags SysOperationRecord +// @Summary 用id查询SysOperationRecord +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query system.SysOperationRecord true "Id" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysOperationRecord" +// @Router /sysOperationRecord/findSysOperationRecord [get] +func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) { + var sysOperationRecord system.SysOperationRecord + err := c.ShouldBindQuery(&sysOperationRecord) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(sysOperationRecord, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + reSysOperationRecord, err := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + return + } + response.OkWithDetailed(gin.H{"reSysOperationRecord": reSysOperationRecord}, "查询成功", c) +} + +// GetSysOperationRecordList +// @Tags SysOperationRecord +// @Summary 分页获取SysOperationRecord列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.SysOperationRecordSearch true "页码, 每页大小, 搜索条件" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量" +// @Router /sysOperationRecord/getSysOperationRecordList [get] +func (s *OperationRecordApi) GetSysOperationRecordList(c *gin.Context) { + var pageInfo systemReq.SysOperationRecordSearch + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := operationRecordService.GetSysOperationRecordInfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_params.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_params.go new file mode 100644 index 000000000..45fb1dfb8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_params.go @@ -0,0 +1,171 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type SysParamsApi struct{} + +// CreateSysParams 创建参数 +// @Tags SysParams +// @Summary 创建参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysParams true "创建参数" +// @Success 200 {object} response.Response{msg=string} "创建成功" +// @Router /sysParams/createSysParams [post] +func (sysParamsApi *SysParamsApi) CreateSysParams(c *gin.Context) { + var sysParams system.SysParams + err := c.ShouldBindJSON(&sysParams) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = sysParamsService.CreateSysParams(&sysParams) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败:"+err.Error(), c) + return + } + response.OkWithMessage("创建成功", c) +} + +// DeleteSysParams 删除参数 +// @Tags SysParams +// @Summary 删除参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysParams true "删除参数" +// @Success 200 {object} response.Response{msg=string} "删除成功" +// @Router /sysParams/deleteSysParams [delete] +func (sysParamsApi *SysParamsApi) DeleteSysParams(c *gin.Context) { + ID := c.Query("ID") + err := sysParamsService.DeleteSysParams(ID) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// DeleteSysParamsByIds 批量删除参数 +// @Tags SysParams +// @Summary 批量删除参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "批量删除成功" +// @Router /sysParams/deleteSysParamsByIds [delete] +func (sysParamsApi *SysParamsApi) DeleteSysParamsByIds(c *gin.Context) { + IDs := c.QueryArray("IDs[]") + err := sysParamsService.DeleteSysParamsByIds(IDs) + if err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败:"+err.Error(), c) + return + } + response.OkWithMessage("批量删除成功", c) +} + +// UpdateSysParams 更新参数 +// @Tags SysParams +// @Summary 更新参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysParams true "更新参数" +// @Success 200 {object} response.Response{msg=string} "更新成功" +// @Router /sysParams/updateSysParams [put] +func (sysParamsApi *SysParamsApi) UpdateSysParams(c *gin.Context) { + var sysParams system.SysParams + err := c.ShouldBindJSON(&sysParams) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = sysParamsService.UpdateSysParams(sysParams) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败:"+err.Error(), c) + return + } + response.OkWithMessage("更新成功", c) +} + +// FindSysParams 用id查询参数 +// @Tags SysParams +// @Summary 用id查询参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query system.SysParams true "用id查询参数" +// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "查询成功" +// @Router /sysParams/findSysParams [get] +func (sysParamsApi *SysParamsApi) FindSysParams(c *gin.Context) { + ID := c.Query("ID") + resysParams, err := sysParamsService.GetSysParams(ID) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败:"+err.Error(), c) + return + } + response.OkWithData(resysParams, c) +} + +// GetSysParamsList 分页获取参数列表 +// @Tags SysParams +// @Summary 分页获取参数列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query systemReq.SysParamsSearch true "分页获取参数列表" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功" +// @Router /sysParams/getSysParamsList [get] +func (sysParamsApi *SysParamsApi) GetSysParamsList(c *gin.Context) { + var pageInfo systemReq.SysParamsSearch + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := sysParamsService.GetSysParamsInfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败:"+err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +// GetSysParam 根据key获取参数value +// @Tags SysParams +// @Summary 根据key获取参数value +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param key query string true "key" +// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "获取成功" +// @Router /sysParams/getSysParam [get] +func (sysParamsApi *SysParamsApi) GetSysParam(c *gin.Context) { + k := c.Query("key") + params, err := sysParamsService.GetSysParam(k) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败:"+err.Error(), c) + return + } + response.OkWithDetailed(params, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_system.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_system.go new file mode 100644 index 000000000..aa41c2f46 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_system.go @@ -0,0 +1,88 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type SystemApi struct{} + +// GetSystemConfig +// @Tags System +// @Summary 获取配置文件内容 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {object} response.Response{data=systemRes.SysConfigResponse,msg=string} "获取配置文件内容,返回包括系统配置" +// @Router /system/getSystemConfig [post] +func (s *SystemApi) GetSystemConfig(c *gin.Context) { + config, err := systemConfigService.GetSystemConfig() + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c) +} + +// SetSystemConfig +// @Tags System +// @Summary 设置配置文件内容 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body system.System true "设置配置文件内容" +// @Success 200 {object} response.Response{data=string} "设置配置文件内容" +// @Router /system/setSystemConfig [post] +func (s *SystemApi) SetSystemConfig(c *gin.Context) { + var sys system.System + err := c.ShouldBindJSON(&sys) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = systemConfigService.SetSystemConfig(sys) + if err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + return + } + response.OkWithMessage("设置成功", c) +} + +// ReloadSystem +// @Tags System +// @Summary 重启系统 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "重启系统" +// @Router /system/reloadSystem [post] +func (s *SystemApi) ReloadSystem(c *gin.Context) { + err := utils.Reload() + if err != nil { + global.GVA_LOG.Error("重启系统失败!", zap.Error(err)) + response.FailWithMessage("重启系统失败", c) + return + } + response.OkWithMessage("重启系统成功", c) +} + +// GetServerInfo +// @Tags System +// @Summary 获取服务器信息 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取服务器信息" +// @Router /system/getServerInfo [post] +func (s *SystemApi) GetServerInfo(c *gin.Context) { + server, err := systemConfigService.GetServerInfo() + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(gin.H{"server": server}, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_user.go new file mode 100644 index 000000000..117b8d063 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/api/v1/system/sys_user.go @@ -0,0 +1,484 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common" + "strconv" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/gin-gonic/gin" + "github.com/redis/go-redis/v9" + "go.uber.org/zap" +) + +// Login +// @Tags Base +// @Summary 用户登录 +// @Produce application/json +// @Param data body systemReq.Login true "用户名, 密码, 验证码" +// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间" +// @Router /base/login [post] +func (b *BaseApi) Login(c *gin.Context) { + var l systemReq.Login + err := c.ShouldBindJSON(&l) + key := c.ClientIP() + + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(l, utils.LoginVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + // 判断验证码是否开启 + openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数 + openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间 + v, ok := global.BlackCache.Get(key) + if !ok { + global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut)) + } + + var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v) + + if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) { + u := &system.SysUser{Username: l.Username, Password: l.Password} + user, err := userService.Login(u) + if err != nil { + global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err)) + // 验证码次数+1 + global.BlackCache.Increment(key, 1) + response.FailWithMessage("用户名不存在或者密码错误", c) + return + } + if user.Enable != 1 { + global.GVA_LOG.Error("登陆失败! 用户被禁止登录!") + // 验证码次数+1 + global.BlackCache.Increment(key, 1) + response.FailWithMessage("用户被禁止登录", c) + return + } + b.TokenNext(c, *user) + return + } + // 验证码次数+1 + global.BlackCache.Increment(key, 1) + response.FailWithMessage("验证码错误", c) +} + +// TokenNext 登录以后签发jwt +func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) { + token, claims, err := utils.LoginToken(&user) + if err != nil { + global.GVA_LOG.Error("获取token失败!", zap.Error(err)) + response.FailWithMessage("获取token失败", c) + return + } + if !global.GVA_CONFIG.System.UseMultipoint { + utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix())) + response.OkWithDetailed(systemRes.LoginResponse{ + User: user, + Token: token, + ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000, + }, "登录成功", c) + return + } + + if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil { + if err := jwtService.SetRedisJWT(token, user.Username); err != nil { + global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err)) + response.FailWithMessage("设置登录状态失败", c) + return + } + utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix())) + response.OkWithDetailed(systemRes.LoginResponse{ + User: user, + Token: token, + ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000, + }, "登录成功", c) + } else if err != nil { + global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err)) + response.FailWithMessage("设置登录状态失败", c) + } else { + var blackJWT system.JwtBlacklist + blackJWT.Jwt = jwtStr + if err := jwtService.JsonInBlacklist(blackJWT); err != nil { + response.FailWithMessage("jwt作废失败", c) + return + } + if err := jwtService.SetRedisJWT(token, user.GetUsername()); err != nil { + response.FailWithMessage("设置登录状态失败", c) + return + } + utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix())) + response.OkWithDetailed(systemRes.LoginResponse{ + User: user, + Token: token, + ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000, + }, "登录成功", c) + } +} + +// Register +// @Tags SysUser +// @Summary 用户注册账号 +// @Produce application/json +// @Param data body systemReq.Register true "用户名, 昵称, 密码, 角色ID" +// @Success 200 {object} response.Response{data=systemRes.SysUserResponse,msg=string} "用户注册账号,返回包括用户信息" +// @Router /user/admin_register [post] +func (b *BaseApi) Register(c *gin.Context) { + var r systemReq.Register + err := c.ShouldBindJSON(&r) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(r, utils.RegisterVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + var authorities []system.SysAuthority + for _, v := range r.AuthorityIds { + authorities = append(authorities, system.SysAuthority{ + AuthorityId: v, + }) + } + user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable, Phone: r.Phone, Email: r.Email} + userReturn, err := userService.Register(*user) + if err != nil { + global.GVA_LOG.Error("注册失败!", zap.Error(err)) + response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c) + return + } + response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c) +} + +// ChangePassword +// @Tags SysUser +// @Summary 用户修改密码 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body systemReq.ChangePasswordReq true "用户名, 原密码, 新密码" +// @Success 200 {object} response.Response{msg=string} "用户修改密码" +// @Router /user/changePassword [post] +func (b *BaseApi) ChangePassword(c *gin.Context) { + var req systemReq.ChangePasswordReq + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(req, utils.ChangePasswordVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + uid := utils.GetUserID(c) + u := &system.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password} + _, err = userService.ChangePassword(u, req.NewPassword) + if err != nil { + global.GVA_LOG.Error("修改失败!", zap.Error(err)) + response.FailWithMessage("修改失败,原密码与当前账户不符", c) + return + } + response.OkWithMessage("修改成功", c) +} + +// GetUserList +// @Tags SysUser +// @Summary 分页获取用户列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body systemReq.GetUserList true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取用户列表,返回包括列表,总数,页码,每页数量" +// @Router /user/getUserList [post] +func (b *BaseApi) GetUserList(c *gin.Context) { + var pageInfo systemReq.GetUserList + err := c.ShouldBindJSON(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(pageInfo, utils.PageInfoVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := userService.GetUserInfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +// SetUserAuthority +// @Tags SysUser +// @Summary 更改用户权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body systemReq.SetUserAuth true "用户UUID, 角色ID" +// @Success 200 {object} response.Response{msg=string} "设置用户权限" +// @Router /user/setUserAuthority [post] +func (b *BaseApi) SetUserAuthority(c *gin.Context) { + var sua systemReq.SetUserAuth + err := c.ShouldBindJSON(&sua) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if UserVerifyErr := utils.Verify(sua, utils.SetUserAuthorityVerify); UserVerifyErr != nil { + response.FailWithMessage(UserVerifyErr.Error(), c) + return + } + userID := utils.GetUserID(c) + err = userService.SetUserAuthority(userID, sua.AuthorityId) + if err != nil { + global.GVA_LOG.Error("修改失败!", zap.Error(err)) + response.FailWithMessage(err.Error(), c) + return + } + claims := utils.GetUserInfo(c) + j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名 + claims.AuthorityId = sua.AuthorityId + if token, err := j.CreateToken(*claims); err != nil { + global.GVA_LOG.Error("修改失败!", zap.Error(err)) + response.FailWithMessage(err.Error(), c) + } else { + c.Header("new-token", token) + c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10)) + utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60)) + response.OkWithMessage("修改成功", c) + } +} + +// SetUserAuthorities +// @Tags SysUser +// @Summary 设置用户权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body systemReq.SetUserAuthorities true "用户UUID, 角色ID" +// @Success 200 {object} response.Response{msg=string} "设置用户权限" +// @Router /user/setUserAuthorities [post] +func (b *BaseApi) SetUserAuthorities(c *gin.Context) { + var sua systemReq.SetUserAuthorities + err := c.ShouldBindJSON(&sua) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + authorityID := utils.GetUserAuthorityId(c) + err = userService.SetUserAuthorities(authorityID, sua.ID, sua.AuthorityIds) + if err != nil { + global.GVA_LOG.Error("修改失败!", zap.Error(err)) + response.FailWithMessage("修改失败", c) + return + } + response.OkWithMessage("修改成功", c) +} + +// DeleteUser +// @Tags SysUser +// @Summary 删除用户 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.GetById true "用户ID" +// @Success 200 {object} response.Response{msg=string} "删除用户" +// @Router /user/deleteUser [delete] +func (b *BaseApi) DeleteUser(c *gin.Context) { + var reqId request.GetById + err := c.ShouldBindJSON(&reqId) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(reqId, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + jwtId := utils.GetUserID(c) + if jwtId == uint(reqId.ID) { + response.FailWithMessage("删除失败, 无法删除自己。", c) + return + } + err = userService.DeleteUser(reqId.ID) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// SetUserInfo +// @Tags SysUser +// @Summary 设置用户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息" +// @Router /user/setUserInfo [put] +func (b *BaseApi) SetUserInfo(c *gin.Context) { + var user systemReq.ChangeUserInfo + err := c.ShouldBindJSON(&user) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = utils.Verify(user, utils.IdVerify) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if len(user.AuthorityIds) != 0 { + authorityID := utils.GetUserAuthorityId(c) + err = userService.SetUserAuthorities(authorityID, user.ID, user.AuthorityIds) + if err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + return + } + } + err = userService.SetUserInfo(system.SysUser{ + GVA_MODEL: global.GVA_MODEL{ + ID: user.ID, + }, + NickName: user.NickName, + HeaderImg: user.HeaderImg, + Phone: user.Phone, + Email: user.Email, + Enable: user.Enable, + }) + if err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + return + } + response.OkWithMessage("设置成功", c) +} + +// SetSelfInfo +// @Tags SysUser +// @Summary 设置用户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息" +// @Router /user/SetSelfInfo [put] +func (b *BaseApi) SetSelfInfo(c *gin.Context) { + var user systemReq.ChangeUserInfo + err := c.ShouldBindJSON(&user) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + user.ID = utils.GetUserID(c) + err = userService.SetSelfInfo(system.SysUser{ + GVA_MODEL: global.GVA_MODEL{ + ID: user.ID, + }, + NickName: user.NickName, + HeaderImg: user.HeaderImg, + Phone: user.Phone, + Email: user.Email, + Enable: user.Enable, + }) + if err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + return + } + response.OkWithMessage("设置成功", c) +} + +// SetSelfSetting +// @Tags SysUser +// @Summary 设置用户配置 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body map[string]interface{} true "用户配置数据" +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户配置" +// @Router /user/SetSelfSetting [put] +func (b *BaseApi) SetSelfSetting(c *gin.Context) { + var req common.JSONMap + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + + err = userService.SetSelfSetting(req, utils.GetUserID(c)) + if err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + return + } + response.OkWithMessage("设置成功", c) +} + +// GetUserInfo +// @Tags SysUser +// @Summary 获取用户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取用户信息" +// @Router /user/getUserInfo [get] +func (b *BaseApi) GetUserInfo(c *gin.Context) { + uuid := utils.GetUserUuid(c) + ReqUser, err := userService.GetUserInfo(uuid) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c) +} + +// ResetPassword +// @Tags SysUser +// @Summary 重置用户密码 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body system.SysUser true "ID" +// @Success 200 {object} response.Response{msg=string} "重置用户密码" +// @Router /user/resetPassword [post] +func (b *BaseApi) ResetPassword(c *gin.Context) { + var user system.SysUser + err := c.ShouldBindJSON(&user) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = userService.ResetPassword(user.ID) + if err != nil { + global.GVA_LOG.Error("重置失败!", zap.Error(err)) + response.FailWithMessage("重置失败"+err.Error(), c) + return + } + response.OkWithMessage("重置成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config.docker.yaml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config.docker.yaml new file mode 100644 index 000000000..a7783716f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config.docker.yaml @@ -0,0 +1,220 @@ +# github.com/flipped-aurora/gin-vue-admin/server Global Configuration + +# jwt configuration +jwt: + signing-key: qmPlus + expires-time: 7d + buffer-time: 1d + issuer: qmPlus +# zap logger configuration +zap: + level: info + format: console + prefix: "[github.com/flipped-aurora/gin-vue-admin/server]" + director: log + show-line: true + encode-level: LowercaseColorLevelEncoder + stacktrace-key: stacktrace + log-in-console: true + +# redis configuration +redis: + db: 0 + addr: 177.7.0.14:6379 + password: "" + +# mongo configuration +mongo: + coll: '' + options: '' + database: '' + username: '' + password: '' + min-pool-size: 0 + max-pool-size: 100 + socket-timeout-ms: 0 + connect-timeout-ms: 0 + is-zap: false + hosts: + - host: '' + port: '' + +# email configuration +email: + to: xxx@qq.com + port: 465 + from: xxx@163.com + host: smtp.163.com + is-ssl: true + secret: xxx + nickname: test + +# system configuration +system: + env: public # Change to "develop" to skip authentication for development mode + addr: 8888 + db-type: mysql + oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置 + use-redis: false # 使用redis + use-mongo: false # 使用mongo + use-multipoint: false + # IP限制次数 一个小时15000次 + iplimit-count: 15000 + # IP限制一个小时 + iplimit-time: 3600 + +# captcha configuration +captcha: + key-long: 6 + img-width: 240 + img-height: 80 + open-captcha: 0 # 0代表一直开启,大于0代表限制次数 + open-captcha-timeout: 3600 # open-captcha大于0时才生效 + +# mysql connect configuration +# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master) +mysql: + path: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + max-idle-conns: 10 + max-open-conns: 100 + log-mode: "" + log-zap: false + +# pgsql connect configuration +# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master) +pgsql: + path: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + max-idle-conns: 10 + max-open-conns: 100 + log-mode: "" + log-zap: false + +db-list: + - disable: true # 是否禁用 + type: "" # 数据库的类型,目前支持mysql、pgsql + alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一 + path: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + max-idle-conns: 10 + max-open-conns: 100 + log-mode: "" + log-zap: false + + +# local configuration +local: + path: uploads/file + store-path: uploads/file + +# autocode configuration +autocode: + transfer-restart: true + # root 自动适配项目根目录 + # 请不要手动配置,他会在项目加载的时候识别出根路径 + root: "" + server: /server + server-plug: /plugin/%s + server-api: /api/v1/%s + server-initialize: /initialize + server-model: /model/%s + server-request: /model/%s/request/ + server-router: /router/%s + server-service: /service/%s + web: /web/src + web-api: /api + web-form: /view + web-table: /view + +# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址) +qiniu: + zone: ZoneHuaDong + bucket: "" + img-path: "" + use-https: false + access-key: "" + secret-key: "" + use-cdn-domains: false + +# aliyun oss configuration +aliyun-oss: + endpoint: yourEndpoint + access-key-id: yourAccessKeyId + access-key-secret: yourAccessKeySecret + bucket-name: yourBucketName + bucket-url: yourBucketUrl + base-path: yourBasePath + +# tencent cos configuration +tencent-cos: + bucket: xxxxx-10005608 + region: ap-shanghai + secret-id: your-secret-id + secret-key: your-secret-key + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server + +# aws s3 configuration (minio compatible) +aws-s3: + bucket: xxxxx-10005608 + region: ap-shanghai + endpoint: "" + s3-force-path-style: false + disable-ssl: false + secret-id: your-secret-id + secret-key: your-secret-key + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server + +# huawei obs configuration +hua-wei-obs: + path: you-path + bucket: you-bucket + endpoint: you-endpoint + access-key: you-access-key + secret-key: you-secret-key + +# excel configuration +excel: + dir: ./resource/excel/ + +# timer task db clear table +Timer: + start: true + spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3 + detail: + - tableName: sys_operation_records + compareField: created_at + interval: 2160h + - tableName: jwt_blacklists + compareField: created_at + interval: 168h + +# 跨域配置 +# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用 +cors: + mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝 + whitelist: + - allow-origin: example1.com + allow-headers: content-type + allow-methods: GET, POST + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type + allow-credentials: true # 布尔值 + - allow-origin: example2.com + allow-headers: content-type + allow-methods: GET, POST + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type + allow-credentials: true # 布尔值 \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config.yaml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config.yaml new file mode 100644 index 000000000..84283b8a5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config.yaml @@ -0,0 +1,233 @@ +aliyun-oss: + endpoint: yourEndpoint + access-key-id: yourAccessKeyId + access-key-secret: yourAccessKeySecret + bucket-name: yourBucketName + bucket-url: yourBucketUrl + base-path: yourBasePath +autocode: + web: web/src + root: /Users/archer/project/gin-vue-admin + server: server + module: github.com/flipped-aurora/gin-vue-admin/server + ai-path: "" +aws-s3: + bucket: xxxxx-10005608 + region: ap-shanghai + endpoint: "" + secret-id: your-secret-id + secret-key: your-secret-key + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server + s3-force-path-style: false + disable-ssl: false +captcha: + key-long: 2 + img-width: 240 + img-height: 80 + open-captcha: 0 + open-captcha-timeout: 3600 +cloudflare-r2: + bucket: xxxx0bucket + base-url: https://gin.vue.admin.com + path: uploads + account-id: xxx_account_id + access-key-id: xxx_key_id + secret-access-key: xxx_secret_key +cors: + mode: strict-whitelist + whitelist: + - allow-origin: example1.com + allow-methods: POST, GET + allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type + allow-credentials: true + - allow-origin: example2.com + allow-methods: GET, POST + allow-headers: content-type + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type + allow-credentials: true +db-list: + - type: "" + alias-name: "" + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false + disable: true +disk-list: + - mount-point: / +email: + to: xxx@qq.com + from: xxx@163.com + host: smtp.163.com + secret: xxx + nickname: test + port: 465 + is-ssl: true +excel: + dir: ./resource/excel/ +hua-wei-obs: + path: you-path + bucket: you-bucket + endpoint: you-endpoint + access-key: you-access-key + secret-key: you-secret-key +jwt: + signing-key: 19bda1a3-df54-480c-b7bf-4d533e48d03e + expires-time: 7d + buffer-time: 1d + issuer: qmPlus +local: + path: uploads/file + store-path: uploads/file +mongo: + coll: "" + options: "" + database: "" + username: "" + password: "" + auth-source: "" + min-pool-size: 0 + max-pool-size: 100 + socket-timeout-ms: 0 + connect-timeout-ms: 0 + is-zap: false + hosts: + - host: "" + port: "" +mssql: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +mysql: + prefix: "" + port: "3306" + config: charset=utf8mb4&parseTime=True&loc=Local + db-name: gva + username: root + password: "123456" + path: 127.0.0.1 + engine: "" + log-mode: error + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +oracle: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +pgsql: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +qiniu: + zone: ZoneHuaDong + bucket: "" + img-path: "" + access-key: "" + secret-key: "" + use-https: false + use-cdn-domains: false +redis: + name: "" + addr: 127.0.0.1:6379 + password: "" + db: 0 + useCluster: false + clusterAddrs: + - 172.21.0.3:7000 + - 172.21.0.4:7001 + - 172.21.0.2:7002 +redis-list: + - name: cache + addr: 127.0.0.1:6379 + password: "" + db: 0 + useCluster: false + clusterAddrs: + - 172.21.0.3:7000 + - 172.21.0.4:7001 + - 172.21.0.2:7002 +sqlite: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +system: + db-type: mysql + oss-type: local + router-prefix: "" + addr: 4000 + iplimit-count: 15000 + iplimit-time: 3600 + use-multipoint: false + use-redis: false + use-mongo: false + use-strict-auth: false +tencent-cos: + bucket: xxxxx-10005608 + region: ap-shanghai + secret-id: your-secret-id + secret-key: your-secret-key + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server +zap: + level: info + prefix: '[github.com/flipped-aurora/gin-vue-admin/server]' + format: console + director: log + encode-level: LowercaseColorLevelEncoder + stacktrace-key: stacktrace + show-line: true + log-in-console: true + retention-day: -1 diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/auto_code.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/auto_code.go new file mode 100644 index 000000000..ade79a023 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/auto_code.go @@ -0,0 +1,22 @@ +package config + +import ( + "path/filepath" + "strings" +) + +type Autocode struct { + Web string `mapstructure:"web" json:"web" yaml:"web"` + Root string `mapstructure:"root" json:"root" yaml:"root"` + Server string `mapstructure:"server" json:"server" yaml:"server"` + Module string `mapstructure:"module" json:"module" yaml:"module"` + AiPath string `mapstructure:"ai-path" json:"ai-path" yaml:"ai-path"` +} + +func (a *Autocode) WebRoot() string { + webs := strings.Split(a.Web, "/") + if len(webs) == 0 { + webs = strings.Split(a.Web, "\\") + } + return filepath.Join(webs...) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/captcha.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/captcha.go new file mode 100644 index 000000000..074a9bfad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/captcha.go @@ -0,0 +1,9 @@ +package config + +type Captcha struct { + KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度 + ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度 + ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度 + OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码 + OpenCaptchaTimeOut int `mapstructure:"open-captcha-timeout" json:"open-captcha-timeout" yaml:"open-captcha-timeout"` // 防爆破验证码超时时间,单位:s(秒) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/config.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/config.go new file mode 100644 index 000000000..6519333cb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/config.go @@ -0,0 +1,36 @@ +package config + +type Server struct { + JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"` + Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"` + Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"` + RedisList []Redis `mapstructure:"redis-list" json:"redis-list" yaml:"redis-list"` + Mongo Mongo `mapstructure:"mongo" json:"mongo" yaml:"mongo"` + Email Email `mapstructure:"email" json:"email" yaml:"email"` + System System `mapstructure:"system" json:"system" yaml:"system"` + Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"` + // auto + AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"` + // gorm + Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"` + Mssql Mssql `mapstructure:"mssql" json:"mssql" yaml:"mssql"` + Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"` + Oracle Oracle `mapstructure:"oracle" json:"oracle" yaml:"oracle"` + Sqlite Sqlite `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"` + DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"` + // oss + Local Local `mapstructure:"local" json:"local" yaml:"local"` + Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"` + AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyun-oss" yaml:"aliyun-oss"` + HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"hua-wei-obs" yaml:"hua-wei-obs"` + TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"` + AwsS3 AwsS3 `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"` + CloudflareR2 CloudflareR2 `mapstructure:"cloudflare-r2" json:"cloudflare-r2" yaml:"cloudflare-r2"` + + Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"` + + DiskList []DiskList `mapstructure:"disk-list" json:"disk-list" yaml:"disk-list"` + + // 跨域配置 + Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/cors.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/cors.go new file mode 100644 index 000000000..7fba99346 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/cors.go @@ -0,0 +1,14 @@ +package config + +type CORS struct { + Mode string `mapstructure:"mode" json:"mode" yaml:"mode"` + Whitelist []CORSWhitelist `mapstructure:"whitelist" json:"whitelist" yaml:"whitelist"` +} + +type CORSWhitelist struct { + AllowOrigin string `mapstructure:"allow-origin" json:"allow-origin" yaml:"allow-origin"` + AllowMethods string `mapstructure:"allow-methods" json:"allow-methods" yaml:"allow-methods"` + AllowHeaders string `mapstructure:"allow-headers" json:"allow-headers" yaml:"allow-headers"` + ExposeHeaders string `mapstructure:"expose-headers" json:"expose-headers" yaml:"expose-headers"` + AllowCredentials bool `mapstructure:"allow-credentials" json:"allow-credentials" yaml:"allow-credentials"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/db_list.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/db_list.go new file mode 100644 index 000000000..39767f53b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/db_list.go @@ -0,0 +1,52 @@ +package config + +import ( + "gorm.io/gorm/logger" + "strings" +) + +type DsnProvider interface { + Dsn() string +} + +// Embeded 结构体可以压平到上一层,从而保持 config 文件的结构和原来一样 +// 见 playground: https://go.dev/play/p/KIcuhqEoxmY + +// GeneralDB 也被 Pgsql 和 Mysql 原样使用 +type GeneralDB struct { + Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 数据库前缀 + Port string `mapstructure:"port" json:"port" yaml:"port"` // 数据库端口 + Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置 + Dbname string `mapstructure:"db-name" json:"db-name" yaml:"db-name"` // 数据库名 + Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库账号 + Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码 + Path string `mapstructure:"path" json:"path" yaml:"path"` // 数据库地址 + Engine string `mapstructure:"engine" json:"engine" yaml:"engine" default:"InnoDB"` // 数据库引擎,默认InnoDB + LogMode string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"` // 是否开启Gorm全局日志 + MaxIdleConns int `mapstructure:"max-idle-conns" json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数 + MaxOpenConns int `mapstructure:"max-open-conns" json:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数 + Singular bool `mapstructure:"singular" json:"singular" yaml:"singular"` // 是否开启全局禁用复数,true表示开启 + LogZap bool `mapstructure:"log-zap" json:"log-zap" yaml:"log-zap"` // 是否通过zap写入日志文件 +} + +func (c GeneralDB) LogLevel() logger.LogLevel { + switch strings.ToLower(c.LogMode) { + case "silent", "Silent": + return logger.Silent + case "error", "Error": + return logger.Error + case "warn", "Warn": + return logger.Warn + case "info", "Info": + return logger.Info + default: + return logger.Info + } +} + +type SpecializedDB struct { + Type string `mapstructure:"type" json:"type" yaml:"type"` + AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"` + GeneralDB `yaml:",inline" mapstructure:",squash"` + Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/disk.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/disk.go new file mode 100644 index 000000000..59a633259 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/disk.go @@ -0,0 +1,9 @@ +package config + +type Disk struct { + MountPoint string `mapstructure:"mount-point" json:"mount-point" yaml:"mount-point"` +} + +type DiskList struct { + Disk `yaml:",inline" mapstructure:",squash"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/email.go new file mode 100644 index 000000000..0984616b2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/email.go @@ -0,0 +1,11 @@ +package config + +type Email struct { + To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 + From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱 + Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 + Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 + Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱 + Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 + IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/excel.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/excel.go new file mode 100644 index 000000000..13caab7f5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/excel.go @@ -0,0 +1,5 @@ +package config + +type Excel struct { + Dir string `mapstructure:"dir" json:"dir" yaml:"dir"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_mssql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_mssql.go new file mode 100644 index 000000000..d18711948 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_mssql.go @@ -0,0 +1,10 @@ +package config + +type Mssql struct { + GeneralDB `yaml:",inline" mapstructure:",squash"` +} + +// Dsn "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm" +func (m *Mssql) Dsn() string { + return "sqlserver://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "?database=" + m.Dbname + "&encrypt=disable" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_mysql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_mysql.go new file mode 100644 index 000000000..77e024539 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_mysql.go @@ -0,0 +1,9 @@ +package config + +type Mysql struct { + GeneralDB `yaml:",inline" mapstructure:",squash"` +} + +func (m *Mysql) Dsn() string { + return m.Username + ":" + m.Password + "@tcp(" + m.Path + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_oracle.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_oracle.go new file mode 100644 index 000000000..1bbeb46ab --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_oracle.go @@ -0,0 +1,10 @@ +package config + +type Oracle struct { + GeneralDB `yaml:",inline" mapstructure:",squash"` +} + +func (m *Oracle) Dsn() string { + return "oracle://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "/" + m.Dbname + "?" + m.Config + +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_pgsql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_pgsql.go new file mode 100644 index 000000000..29fe03f43 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_pgsql.go @@ -0,0 +1,17 @@ +package config + +type Pgsql struct { + GeneralDB `yaml:",inline" mapstructure:",squash"` +} + +// Dsn 基于配置文件获取 dsn +// Author [SliverHorn](https://github.com/SliverHorn) +func (p *Pgsql) Dsn() string { + return "host=" + p.Path + " user=" + p.Username + " password=" + p.Password + " dbname=" + p.Dbname + " port=" + p.Port + " " + p.Config +} + +// LinkDsn 根据 dbname 生成 dsn +// Author [SliverHorn](https://github.com/SliverHorn) +func (p *Pgsql) LinkDsn(dbname string) string { + return "host=" + p.Path + " user=" + p.Username + " password=" + p.Password + " dbname=" + dbname + " port=" + p.Port + " " + p.Config +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_sqlite.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_sqlite.go new file mode 100644 index 000000000..46f2e19a5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/gorm_sqlite.go @@ -0,0 +1,13 @@ +package config + +import ( + "path/filepath" +) + +type Sqlite struct { + GeneralDB `yaml:",inline" mapstructure:",squash"` +} + +func (s *Sqlite) Dsn() string { + return filepath.Join(s.Path, s.Dbname+".db") +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/jwt.go new file mode 100644 index 000000000..c95d30dc1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/jwt.go @@ -0,0 +1,8 @@ +package config + +type JWT struct { + SigningKey string `mapstructure:"signing-key" json:"signing-key" yaml:"signing-key"` // jwt签名 + ExpiresTime string `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间 + BufferTime string `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"` // 缓冲时间 + Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/mongo.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/mongo.go new file mode 100644 index 000000000..2034a3fb4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/mongo.go @@ -0,0 +1,41 @@ +package config + +import ( + "fmt" + "strings" +) + +type Mongo struct { + Coll string `json:"coll" yaml:"coll" mapstructure:"coll"` // collection name + Options string `json:"options" yaml:"options" mapstructure:"options"` // mongodb options + Database string `json:"database" yaml:"database" mapstructure:"database"` // database name + Username string `json:"username" yaml:"username" mapstructure:"username"` // 用户名 + Password string `json:"password" yaml:"password" mapstructure:"password"` // 密码 + AuthSource string `json:"auth-source" yaml:"auth-source" mapstructure:"auth-source"` // 验证数据库 + MinPoolSize uint64 `json:"min-pool-size" yaml:"min-pool-size" mapstructure:"min-pool-size"` // 最小连接池 + MaxPoolSize uint64 `json:"max-pool-size" yaml:"max-pool-size" mapstructure:"max-pool-size"` // 最大连接池 + SocketTimeoutMs int64 `json:"socket-timeout-ms" yaml:"socket-timeout-ms" mapstructure:"socket-timeout-ms"` // socket超时时间 + ConnectTimeoutMs int64 `json:"connect-timeout-ms" yaml:"connect-timeout-ms" mapstructure:"connect-timeout-ms"` // 连接超时时间 + IsZap bool `json:"is-zap" yaml:"is-zap" mapstructure:"is-zap"` // 是否开启zap日志 + Hosts []*MongoHost `json:"hosts" yaml:"hosts" mapstructure:"hosts"` // 主机列表 +} + +type MongoHost struct { + Host string `json:"host" yaml:"host" mapstructure:"host"` // ip地址 + Port string `json:"port" yaml:"port" mapstructure:"port"` // 端口 +} + +// Uri . +func (x *Mongo) Uri() string { + length := len(x.Hosts) + hosts := make([]string, 0, length) + for i := 0; i < length; i++ { + if x.Hosts[i].Host != "" && x.Hosts[i].Port != "" { + hosts = append(hosts, x.Hosts[i].Host+":"+x.Hosts[i].Port) + } + } + if x.Options != "" { + return fmt.Sprintf("mongodb://%s/%s?%s", strings.Join(hosts, ","), x.Database, x.Options) + } + return fmt.Sprintf("mongodb://%s/%s", strings.Join(hosts, ","), x.Database) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_aliyun.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_aliyun.go new file mode 100644 index 000000000..934bd782a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_aliyun.go @@ -0,0 +1,10 @@ +package config + +type AliyunOSS struct { + Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` + AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"` + AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"` + BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"` + BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"` + BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_aws.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_aws.go new file mode 100644 index 000000000..7ec6acc54 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_aws.go @@ -0,0 +1,13 @@ +package config + +type AwsS3 struct { + Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` + Region string `mapstructure:"region" json:"region" yaml:"region"` + Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` + SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"` + SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` + BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"` + PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"` + S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"` + DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_cloudflare.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_cloudflare.go new file mode 100644 index 000000000..ab7a393dd --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_cloudflare.go @@ -0,0 +1,10 @@ +package config + +type CloudflareR2 struct { + Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` + BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"` + Path string `mapstructure:"path" json:"path" yaml:"path"` + AccountID string `mapstructure:"account-id" json:"account-id" yaml:"account-id"` + AccessKeyID string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"` + SecretAccessKey string `mapstructure:"secret-access-key" json:"secret-access-key" yaml:"secret-access-key"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_huawei.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_huawei.go new file mode 100644 index 000000000..45dfbcdb0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_huawei.go @@ -0,0 +1,9 @@ +package config + +type HuaWeiObs struct { + Path string `mapstructure:"path" json:"path" yaml:"path"` + Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` + Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` + AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"` + SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_local.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_local.go new file mode 100644 index 000000000..7038d4ad9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_local.go @@ -0,0 +1,6 @@ +package config + +type Local struct { + Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件访问路径 + StorePath string `mapstructure:"store-path" json:"store-path" yaml:"store-path"` // 本地文件存储路径 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_qiniu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_qiniu.go new file mode 100644 index 000000000..298fe2d3c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_qiniu.go @@ -0,0 +1,11 @@ +package config + +type Qiniu struct { + Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域 + Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称 + ImgPath string `mapstructure:"img-path" json:"img-path" yaml:"img-path"` // CDN加速域名 + AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"` // 秘钥AK + SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` // 秘钥SK + UseHTTPS bool `mapstructure:"use-https" json:"use-https" yaml:"use-https"` // 是否使用https + UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"use-cdn-domains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_tencent.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_tencent.go new file mode 100644 index 000000000..39a29d116 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/oss_tencent.go @@ -0,0 +1,10 @@ +package config + +type TencentCOS struct { + Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` + Region string `mapstructure:"region" json:"region" yaml:"region"` + SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"` + SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` + BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"` + PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/redis.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/redis.go new file mode 100644 index 000000000..94b5bf6b5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/redis.go @@ -0,0 +1,10 @@ +package config + +type Redis struct { + Name string `mapstructure:"name" json:"name" yaml:"name"` // 代表当前实例的名字 + Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口 + Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码 + DB int `mapstructure:"db" json:"db" yaml:"db"` // 单实例模式下redis的哪个数据库 + UseCluster bool `mapstructure:"useCluster" json:"useCluster" yaml:"useCluster"` // 是否使用集群模式 + ClusterAddrs []string `mapstructure:"clusterAddrs" json:"clusterAddrs" yaml:"clusterAddrs"` // 集群模式下的节点地址列表 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/system.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/system.go new file mode 100644 index 000000000..b47dbf64e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/system.go @@ -0,0 +1,14 @@ +package config + +type System struct { + DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql + OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // Oss类型 + RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"` + Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值 + LimitCountIP int `mapstructure:"iplimit-count" json:"iplimit-count" yaml:"iplimit-count"` + LimitTimeIP int `mapstructure:"iplimit-time" json:"iplimit-time" yaml:"iplimit-time"` + UseMultipoint bool `mapstructure:"use-multipoint" json:"use-multipoint" yaml:"use-multipoint"` // 多点登录拦截 + UseRedis bool `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"` // 使用redis + UseMongo bool `mapstructure:"use-mongo" json:"use-mongo" yaml:"use-mongo"` // 使用mongo + UseStrictAuth bool `mapstructure:"use-strict-auth" json:"use-strict-auth" yaml:"use-strict-auth"` // 使用树形角色分配模式 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/zap.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/zap.go new file mode 100644 index 000000000..0e8ae2b29 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/config/zap.go @@ -0,0 +1,71 @@ +package config + +import ( + "go.uber.org/zap/zapcore" + "time" +) + +type Zap struct { + Level string `mapstructure:"level" json:"level" yaml:"level"` // 级别 + Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 日志前缀 + Format string `mapstructure:"format" json:"format" yaml:"format"` // 输出 + Director string `mapstructure:"director" json:"director" yaml:"director"` // 日志文件夹 + EncodeLevel string `mapstructure:"encode-level" json:"encode-level" yaml:"encode-level"` // 编码级 + StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktrace-key" yaml:"stacktrace-key"` // 栈名 + ShowLine bool `mapstructure:"show-line" json:"show-line" yaml:"show-line"` // 显示行 + LogInConsole bool `mapstructure:"log-in-console" json:"log-in-console" yaml:"log-in-console"` // 输出控制台 + RetentionDay int `mapstructure:"retention-day" json:"retention-day" yaml:"retention-day"` // 日志保留天数 +} + +// Levels 根据字符串转化为 zapcore.Levels +func (c *Zap) Levels() []zapcore.Level { + levels := make([]zapcore.Level, 0, 7) + level, err := zapcore.ParseLevel(c.Level) + if err != nil { + level = zapcore.DebugLevel + } + for ; level <= zapcore.FatalLevel; level++ { + levels = append(levels, level) + } + return levels +} + +func (c *Zap) Encoder() zapcore.Encoder { + config := zapcore.EncoderConfig{ + TimeKey: "time", + NameKey: "name", + LevelKey: "level", + CallerKey: "caller", + MessageKey: "message", + StacktraceKey: c.StacktraceKey, + LineEnding: zapcore.DefaultLineEnding, + EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) { + encoder.AppendString(c.Prefix + t.Format("2006-01-02 15:04:05.000")) + }, + EncodeLevel: c.LevelEncoder(), + EncodeCaller: zapcore.FullCallerEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + } + if c.Format == "json" { + return zapcore.NewJSONEncoder(config) + } + return zapcore.NewConsoleEncoder(config) + +} + +// LevelEncoder 根据 EncodeLevel 返回 zapcore.LevelEncoder +// Author [SliverHorn](https://github.com/SliverHorn) +func (c *Zap) LevelEncoder() zapcore.LevelEncoder { + switch { + case c.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认) + return zapcore.LowercaseLevelEncoder + case c.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色 + return zapcore.LowercaseColorLevelEncoder + case c.EncodeLevel == "CapitalLevelEncoder": // 大写编码器 + return zapcore.CapitalLevelEncoder + case c.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色 + return zapcore.CapitalColorLevelEncoder + default: + return zapcore.LowercaseLevelEncoder + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/constant.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/constant.go new file mode 100644 index 000000000..b22362cc7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/constant.go @@ -0,0 +1,9 @@ +package internal + +const ( + ConfigEnv = "GVA_CONFIG" + ConfigDefaultFile = "config.yaml" + ConfigTestFile = "config.test.yaml" + ConfigDebugFile = "config.debug.yaml" + ConfigReleaseFile = "config.release.yaml" +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/cutter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/cutter.go new file mode 100644 index 000000000..e053af6e5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/cutter.go @@ -0,0 +1,121 @@ +package internal + +import ( + "os" + "path/filepath" + "sync" + "time" +) + +// Cutter 实现 io.Writer 接口 +// 用于日志切割, strings.Join([]string{director,layout, formats..., level+".log"}, os.PathSeparator) +type Cutter struct { + level string // 日志级别(debug, info, warn, error, dpanic, panic, fatal) + layout string // 时间格式 2006-01-02 15:04:05 + formats []string // 自定义参数([]string{Director,"2006-01-02", "business"(此参数可不写), level+".log"} + director string // 日志文件夹 + retentionDay int //日志保留天数 + file *os.File // 文件句柄 + mutex *sync.RWMutex // 读写锁 +} + +type CutterOption func(*Cutter) + +// CutterWithLayout 时间格式 +func CutterWithLayout(layout string) CutterOption { + return func(c *Cutter) { + c.layout = layout + } +} + +// CutterWithFormats 格式化参数 +func CutterWithFormats(format ...string) CutterOption { + return func(c *Cutter) { + if len(format) > 0 { + c.formats = format + } + } +} + +func NewCutter(director string, level string, retentionDay int, options ...CutterOption) *Cutter { + rotate := &Cutter{ + level: level, + director: director, + retentionDay: retentionDay, + mutex: new(sync.RWMutex), + } + for i := 0; i < len(options); i++ { + options[i](rotate) + } + return rotate +} + +// Write satisfies the io.Writer interface. It writes to the +// appropriate file handle that is currently being used. +// If we have reached rotation time, the target file gets +// automatically rotated, and also purged if necessary. +func (c *Cutter) Write(bytes []byte) (n int, err error) { + c.mutex.Lock() + defer func() { + if c.file != nil { + _ = c.file.Close() + c.file = nil + } + c.mutex.Unlock() + }() + length := len(c.formats) + values := make([]string, 0, 3+length) + values = append(values, c.director) + if c.layout != "" { + values = append(values, time.Now().Format(c.layout)) + } + for i := 0; i < length; i++ { + values = append(values, c.formats[i]) + } + values = append(values, c.level+".log") + filename := filepath.Join(values...) + director := filepath.Dir(filename) + err = os.MkdirAll(director, os.ModePerm) + if err != nil { + return 0, err + } + err = removeNDaysFolders(c.director, c.retentionDay) + if err != nil { + return 0, err + } + c.file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return 0, err + } + return c.file.Write(bytes) +} + +func (c *Cutter) Sync() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.file != nil { + return c.file.Sync() + } + return nil +} + +// 增加日志目录文件清理 小于等于零的值默认忽略不再处理 +func removeNDaysFolders(dir string, days int) error { + if days <= 0 { + return nil + } + cutoff := time.Now().AddDate(0, 0, -days) + return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() && info.ModTime().Before(cutoff) && path != dir { + err = os.RemoveAll(path) + if err != nil { + return err + } + } + return nil + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/zap_core.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/zap_core.go new file mode 100644 index 000000000..4648e60cb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/internal/zap_core.go @@ -0,0 +1,68 @@ +package internal + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "time" +) + +type ZapCore struct { + level zapcore.Level + zapcore.Core +} + +func NewZapCore(level zapcore.Level) *ZapCore { + entity := &ZapCore{level: level} + syncer := entity.WriteSyncer() + levelEnabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool { + return l == level + }) + entity.Core = zapcore.NewCore(global.GVA_CONFIG.Zap.Encoder(), syncer, levelEnabler) + return entity +} + +func (z *ZapCore) WriteSyncer(formats ...string) zapcore.WriteSyncer { + cutter := NewCutter( + global.GVA_CONFIG.Zap.Director, + z.level.String(), + global.GVA_CONFIG.Zap.RetentionDay, + CutterWithLayout(time.DateOnly), + CutterWithFormats(formats...), + ) + if global.GVA_CONFIG.Zap.LogInConsole { + multiSyncer := zapcore.NewMultiWriteSyncer(os.Stdout, cutter) + return zapcore.AddSync(multiSyncer) + } + return zapcore.AddSync(cutter) +} + +func (z *ZapCore) Enabled(level zapcore.Level) bool { + return z.level == level +} + +func (z *ZapCore) With(fields []zapcore.Field) zapcore.Core { + return z.Core.With(fields) +} + +func (z *ZapCore) Check(entry zapcore.Entry, check *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if z.Enabled(entry.Level) { + return check.AddCore(entry, z) + } + return check +} + +func (z *ZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { + for i := 0; i < len(fields); i++ { + if fields[i].Key == "business" || fields[i].Key == "folder" || fields[i].Key == "directory" { + syncer := z.WriteSyncer(fields[i].String) + z.Core = zapcore.NewCore(global.GVA_CONFIG.Zap.Encoder(), syncer, z.level) + } + } + return z.Core.Write(entry, fields) +} + +func (z *ZapCore) Sync() error { + return z.Core.Sync() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server.go new file mode 100644 index 000000000..13bebf4e3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server.go @@ -0,0 +1,45 @@ +package core + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "go.uber.org/zap" +) + +type server interface { + ListenAndServe() error +} + +func RunWindowsServer() { + if global.GVA_CONFIG.System.UseMultipoint || global.GVA_CONFIG.System.UseRedis { + // 初始化redis服务 + initialize.Redis() + initialize.RedisList() + } + + if global.GVA_CONFIG.System.UseMongo { + err := initialize.Mongo.Initialization() + if err != nil { + zap.L().Error(fmt.Sprintf("%+v", err)) + } + } + // 从db加载jwt数据 + if global.GVA_DB != nil { + system.LoadAll() + } + + Router := initialize.Routers() + + address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr) + s := initServer(address, Router) + + global.GVA_LOG.Info("server run success on ", zap.String("address", address)) + + fmt.Printf(` + 默认自动化文档地址:http://127.0.0.1%s/swagger/index.html + 默认前端文件运行地址:http://127.0.0.1:8080 +`, address) + global.GVA_LOG.Error(s.ListenAndServe().Error()) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server_other.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server_other.go new file mode 100644 index 000000000..83645fced --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server_other.go @@ -0,0 +1,19 @@ +//go:build !windows +// +build !windows + +package core + +import ( + "time" + + "github.com/fvbock/endless" + "github.com/gin-gonic/gin" +) + +func initServer(address string, router *gin.Engine) server { + s := endless.NewServer(address, router) + s.ReadHeaderTimeout = 10 * time.Minute + s.WriteTimeout = 10 * time.Minute + s.MaxHeaderBytes = 1 << 20 + return s +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server_win.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server_win.go new file mode 100644 index 000000000..20cf44b9f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/server_win.go @@ -0,0 +1,21 @@ +//go:build windows +// +build windows + +package core + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func initServer(address string, router *gin.Engine) server { + return &http.Server{ + Addr: address, + Handler: router, + ReadTimeout: 10 * time.Minute, + WriteTimeout: 10 * time.Minute, + MaxHeaderBytes: 1 << 20, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/viper.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/viper.go new file mode 100644 index 000000000..0f3c23afa --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/viper.go @@ -0,0 +1,71 @@ +package core + +import ( + "flag" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/core/internal" + "github.com/gin-gonic/gin" + "os" + "path/filepath" + + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" + + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// Viper // +// 优先级: 命令行 > 环境变量 > 默认值 +// Author [SliverHorn](https://github.com/SliverHorn) +func Viper(path ...string) *viper.Viper { + var config string + + if len(path) == 0 { + flag.StringVar(&config, "c", "", "choose config file.") + flag.Parse() + if config == "" { // 判断命令行参数是否为空 + if configEnv := os.Getenv(internal.ConfigEnv); configEnv == "" { // 判断 internal.ConfigEnv 常量存储的环境变量是否为空 + switch gin.Mode() { + case gin.DebugMode: + config = internal.ConfigDefaultFile + case gin.ReleaseMode: + config = internal.ConfigReleaseFile + case gin.TestMode: + config = internal.ConfigTestFile + } + fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), config) + } else { // internal.ConfigEnv 常量存储的环境变量不为空 将值赋值于config + config = configEnv + fmt.Printf("您正在使用%s环境变量,config的路径为%s\n", internal.ConfigEnv, config) + } + } else { // 命令行参数不为空 将值赋值于config + fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%s\n", config) + } + } else { // 函数传递的可变参数的第一个值赋值于config + config = path[0] + fmt.Printf("您正在使用func Viper()传递的值,config的路径为%s\n", config) + } + + v := viper.New() + v.SetConfigFile(config) + v.SetConfigType("yaml") + err := v.ReadInConfig() + if err != nil { + panic(fmt.Errorf("Fatal error config file: %s \n", err)) + } + v.WatchConfig() + + v.OnConfigChange(func(e fsnotify.Event) { + fmt.Println("config file changed:", e.Name) + if err = v.Unmarshal(&global.GVA_CONFIG); err != nil { + fmt.Println(err) + } + }) + if err = v.Unmarshal(&global.GVA_CONFIG); err != nil { + panic(err) + } + + // root 适配性 根据root位置去找到对应迁移位置,保证root路径有效 + global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") + return v +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/zap.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/zap.go new file mode 100644 index 000000000..d7e08a44a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/core/zap.go @@ -0,0 +1,32 @@ +package core + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/core/internal" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" +) + +// Zap 获取 zap.Logger +// Author [SliverHorn](https://github.com/SliverHorn) +func Zap() (logger *zap.Logger) { + if ok, _ := utils.PathExists(global.GVA_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹 + fmt.Printf("create %v directory\n", global.GVA_CONFIG.Zap.Director) + _ = os.Mkdir(global.GVA_CONFIG.Zap.Director, os.ModePerm) + } + levels := global.GVA_CONFIG.Zap.Levels() + length := len(levels) + cores := make([]zapcore.Core, 0, length) + for i := 0; i < length; i++ { + core := internal.NewZapCore(levels[i]) + cores = append(cores, core) + } + logger = zap.New(zapcore.NewTee(cores...)) + if global.GVA_CONFIG.Zap.ShowLine { + logger = logger.WithOptions(zap.AddCaller()) + } + return logger +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/docs.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/docs.go new file mode 100644 index 000000000..09c1dc1db --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/docs.go @@ -0,0 +1,8104 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/createApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "创建基础api", + "parameters": [ + { + "description": "api路径, api中文描述, api组, 方法", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysApi" + } + } + ], + "responses": { + "200": { + "description": "创建基础api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/deleteApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "删除api", + "parameters": [ + { + "description": "ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysApi" + } + } + ], + "responses": { + "200": { + "description": "删除api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/deleteApisByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "删除选中Api", + "parameters": [ + { + "description": "ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除选中Api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/enterSyncApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "确认同步API", + "responses": { + "200": { + "description": "确认同步API", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/freshCasbin": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "刷新casbin缓存", + "responses": { + "200": { + "description": "刷新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/getAllApis": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "获取所有的Api 不分页", + "responses": { + "200": { + "description": "获取所有的Api 不分页,返回包括api列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAPIListResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/getApiById": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "根据id获取api", + "parameters": [ + { + "description": "根据id获取api", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "根据id获取api,返回包括api详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAPIResponse" + } + } + } + ] + } + } + } + } + }, + "/api/getApiGroups": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "获取API分组", + "responses": { + "200": { + "description": "获取API分组", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/getApiList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "分页获取API列表", + "parameters": [ + { + "description": "分页获取API列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SearchApiParams" + } + } + ], + "responses": { + "200": { + "description": "分页获取API列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/ignoreApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "IgnoreApi" + ], + "summary": "忽略API", + "responses": { + "200": { + "description": "同步API", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/syncApi": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "同步API", + "responses": { + "200": { + "description": "同步API", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/updateApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "修改基础api", + "parameters": [ + { + "description": "api路径, api中文描述, api组, 方法", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysApi" + } + } + ], + "responses": { + "200": { + "description": "修改基础api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/copyAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "拷贝角色", + "parameters": [ + { + "description": "旧角色id, 新权限id, 新权限名, 新父角色id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/response.SysAuthorityCopyResponse" + } + } + ], + "responses": { + "200": { + "description": "拷贝角色,返回包括系统角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/createAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "创建角色", + "parameters": [ + { + "description": "权限id, 权限名, 父角色id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "创建角色,返回包括系统角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/deleteAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "删除角色", + "parameters": [ + { + "description": "删除角色", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "删除角色", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/getAuthorityList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "分页获取角色列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页获取角色列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/setDataAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "设置角色资源权限", + "parameters": [ + { + "description": "设置角色资源权限", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "设置角色资源权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/updateAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "更新角色信息", + "parameters": [ + { + "description": "权限id, 权限名, 父角色id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "更新角色信息,返回包括系统角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authorityBtn/canRemoveAuthorityBtn": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityBtn" + ], + "summary": "设置权限按钮", + "responses": { + "200": { + "description": "删除成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authorityBtn/getAuthorityBtn": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityBtn" + ], + "summary": "获取权限按钮", + "parameters": [ + { + "description": "菜单id, 角色id, 选中的按钮id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAuthorityBtnReq" + } + } + ], + "responses": { + "200": { + "description": "返回列表成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityBtnRes" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authorityBtn/setAuthorityBtn": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityBtn" + ], + "summary": "设置权限按钮", + "parameters": [ + { + "description": "菜单id, 角色id, 选中的按钮id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAuthorityBtnReq" + } + } + ], + "responses": { + "200": { + "description": "返回列表成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/addFunc": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AddFunc" + ], + "summary": "增加方法", + "parameters": [ + { + "description": "增加方法", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AutoCode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/autoCode/createPackage": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePackage" + ], + "summary": "创建package", + "parameters": [ + { + "description": "创建package", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAutoCodePackageCreate" + } + } + ], + "responses": { + "200": { + "description": "创建package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/createTemp": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodeTemplate" + ], + "summary": "自动代码模板", + "parameters": [ + { + "description": "创建自动代码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AutoCode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/autoCode/delPackage": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "删除package", + "parameters": [ + { + "description": "创建package", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/delSysHistory": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "删除回滚记录", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除回滚记录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getColumn": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取当前表所有字段", + "responses": { + "200": { + "description": "获取当前表所有字段", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getDatabase": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取当前所有数据库", + "responses": { + "200": { + "description": "获取当前所有数据库", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getMeta": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取meta信息", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "获取meta信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getPackage": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePackage" + ], + "summary": "获取package", + "responses": { + "200": { + "description": "创建package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getSysHistory": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "查询回滚记录", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "查询回滚记录,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getTables": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取当前数据库所有表", + "responses": { + "200": { + "description": "获取当前数据库所有表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getTemplates": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePackage" + ], + "summary": "获取package", + "responses": { + "200": { + "description": "创建package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/installPlugin": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePlugin" + ], + "summary": "安装插件", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "plug", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "安装插件成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/preview": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodeTemplate" + ], + "summary": "预览创建后的代码", + "parameters": [ + { + "description": "预览创建代码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AutoCode" + } + } + ], + "responses": { + "200": { + "description": "预览创建后的代码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/pubPlug": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePlugin" + ], + "summary": "打包插件", + "parameters": [ + { + "type": "string", + "description": "插件名称", + "name": "plugName", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "打包插件成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/rollback": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "回滚自动生成代码", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAutoHistoryRollBack" + } + } + ], + "responses": { + "200": { + "description": "回滚自动生成代码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/base/captcha": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "生成验证码", + "responses": { + "200": { + "description": "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysCaptchaResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/base/login": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "用户名, 密码, 验证码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Login" + } + } + ], + "responses": { + "200": { + "description": "返回包括用户信息,token,过期时间", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.LoginResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/casbin/UpdateCasbin": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Casbin" + ], + "summary": "更新角色api权限", + "parameters": [ + { + "description": "权限id, 权限模型列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CasbinInReceive" + } + } + ], + "responses": { + "200": { + "description": "更新角色api权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/casbin/getPolicyPathByAuthorityId": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Casbin" + ], + "summary": "获取权限列表", + "parameters": [ + { + "description": "权限id, 权限模型列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CasbinInReceive" + } + } + ], + "responses": { + "200": { + "description": "获取权限列表,返回包括casbin详情列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PolicyPathResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/customer/customer": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "获取单一客户信息", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "客户名", + "name": "customerName", + "in": "query" + }, + { + "type": "string", + "description": "客户手机号", + "name": "customerPhoneData", + "in": "query" + }, + { + "type": "integer", + "description": "管理角色ID", + "name": "sysUserAuthorityID", + "in": "query" + }, + { + "type": "integer", + "description": "管理ID", + "name": "sysUserId", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取单一客户信息,返回包括客户详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.ExaCustomerResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "更新客户信息", + "parameters": [ + { + "description": "客户ID, 客户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + ], + "responses": { + "200": { + "description": "更新客户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "创建客户", + "parameters": [ + { + "description": "客户用户名, 客户手机号码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + ], + "responses": { + "200": { + "description": "创建客户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "删除客户", + "parameters": [ + { + "description": "客户ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + ], + "responses": { + "200": { + "description": "删除客户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/customer/customerList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "分页获取权限客户列表", + "parameters": [ + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "分页获取权限客户列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/email/emailTest": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "发送测试邮件", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/email/sendEmail": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "发送邮件", + "parameters": [ + { + "description": "发送邮件必须的参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/response.Email" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/fileUploadAndDownload/breakpointContinue": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "断点续传到服务器", + "parameters": [ + { + "type": "file", + "description": "an example for breakpoint resume, 断点续传示例", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "断点续传到服务器", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/deleteFile": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "删除文件", + "parameters": [ + { + "description": "传入文件里面id即可", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaFileUploadAndDownload" + } + } + ], + "responses": { + "200": { + "description": "删除文件", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/findFile": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "创建文件", + "parameters": [ + { + "type": "file", + "description": "上传文件完成", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "创建文件,返回包括文件路径", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.FilePathResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/getFileList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "分页文件列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页文件列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/removeChunk": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "删除切片", + "parameters": [ + { + "type": "file", + "description": "删除缓存切片", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "删除切片", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/upload": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "上传文件示例", + "parameters": [ + { + "type": "file", + "description": "上传文件示例", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "上传文件示例,返回包括文件详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.ExaFileResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/createInfo": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "创建公告", + "parameters": [ + { + "description": "创建公告", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Info" + } + } + ], + "responses": { + "200": { + "description": "创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/deleteInfo": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "删除公告", + "parameters": [ + { + "description": "删除公告", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Info" + } + } + ], + "responses": { + "200": { + "description": "删除成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/deleteInfoByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "批量删除公告", + "responses": { + "200": { + "description": "批量删除成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/findInfo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "用id查询公告", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "内容", + "name": "content", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "标题", + "name": "title", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "integer", + "description": "作者", + "name": "userID", + "in": "query" + } + ], + "responses": { + "200": { + "description": "查询成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Info" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/getInfoDataSource": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "获取Info的数据源", + "responses": { + "200": { + "description": "查询成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/getInfoList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "分页获取公告列表", + "parameters": [ + { + "type": "string", + "name": "endCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "name": "startCreatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/getInfoPublic": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "不需要鉴权的公告接口", + "parameters": [ + { + "type": "string", + "name": "endCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "name": "startCreatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/updateInfo": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "更新公告", + "parameters": [ + { + "description": "更新公告", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Info" + } + } + ], + "responses": { + "200": { + "description": "更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/init/checkdb": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "CheckDB" + ], + "summary": "初始化用户数据库", + "responses": { + "200": { + "description": "初始化用户数据库", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/init/initdb": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "InitDB" + ], + "summary": "初始化用户数据库", + "parameters": [ + { + "description": "初始化数据库参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.InitDB" + } + } + ], + "responses": { + "200": { + "description": "初始化用户数据库", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/jwt/jsonInBlacklist": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Jwt" + ], + "summary": "jwt加入黑名单", + "responses": { + "200": { + "description": "jwt加入黑名单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/addBaseMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "新增菜单", + "parameters": [ + { + "description": "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + ], + "responses": { + "200": { + "description": "新增菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/addMenuAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "增加menu和角色关联关系", + "parameters": [ + { + "description": "角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AddMenuAuthorityInfo" + } + } + ], + "responses": { + "200": { + "description": "增加menu和角色关联关系", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/deleteBaseMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "删除菜单", + "parameters": [ + { + "description": "菜单id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getBaseMenuById": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "根据id获取菜单", + "parameters": [ + { + "description": "菜单id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "根据id获取菜单,返回包括系统菜单列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysBaseMenuResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getBaseMenuTree": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取用户动态路由", + "parameters": [ + { + "description": "空", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Empty" + } + } + ], + "responses": { + "200": { + "description": "获取用户动态路由,返回包括系统菜单列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysBaseMenusResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取用户动态路由", + "parameters": [ + { + "description": "空", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Empty" + } + } + ], + "responses": { + "200": { + "description": "获取用户动态路由,返回包括系统菜单详情列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysMenusResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getMenuAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取指定角色menu", + "parameters": [ + { + "description": "角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetAuthorityId" + } + } + ], + "responses": { + "200": { + "description": "获取指定角色menu", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getMenuList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "分页获取基础menu列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页获取基础menu列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/updateBaseMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "更新菜单", + "parameters": [ + { + "description": "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + ], + "responses": { + "200": { + "description": "更新菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/createSysDictionary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "创建SysDictionary", + "parameters": [ + { + "description": "SysDictionary模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionary" + } + } + ], + "responses": { + "200": { + "description": "创建SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/deleteSysDictionary": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "删除SysDictionary", + "parameters": [ + { + "description": "SysDictionary模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionary" + } + } + ], + "responses": { + "200": { + "description": "删除SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/findSysDictionary": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "用id查询SysDictionary", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "描述", + "name": "desc", + "in": "query" + }, + { + "type": "string", + "description": "字典名(中)", + "name": "name", + "in": "query" + }, + { + "type": "boolean", + "description": "状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "字典名(英)", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "用id查询SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/getSysDictionaryList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "分页获取SysDictionary列表", + "responses": { + "200": { + "description": "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/updateSysDictionary": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "更新SysDictionary", + "parameters": [ + { + "description": "SysDictionary模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionary" + } + } + ], + "responses": { + "200": { + "description": "更新SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/createSysDictionaryDetail": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "创建SysDictionaryDetail", + "parameters": [ + { + "description": "SysDictionaryDetail模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + } + ], + "responses": { + "200": { + "description": "创建SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/deleteSysDictionaryDetail": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "删除SysDictionaryDetail", + "parameters": [ + { + "description": "SysDictionaryDetail模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + } + ], + "responses": { + "200": { + "description": "删除SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/findSysDictionaryDetail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "用id查询SysDictionaryDetail", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "扩展值", + "name": "extend", + "in": "query" + }, + { + "type": "string", + "description": "展示值", + "name": "label", + "in": "query" + }, + { + "type": "integer", + "description": "排序标记", + "name": "sort", + "in": "query" + }, + { + "type": "boolean", + "description": "启用状态", + "name": "status", + "in": "query" + }, + { + "type": "integer", + "description": "关联标记", + "name": "sysDictionaryID", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "string", + "description": "字典值", + "name": "value", + "in": "query" + } + ], + "responses": { + "200": { + "description": "用id查询SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getSysDictionaryDetailList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "分页获取SysDictionaryDetail列表", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "扩展值", + "name": "extend", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "string", + "description": "展示值", + "name": "label", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "integer", + "description": "排序标记", + "name": "sort", + "in": "query" + }, + { + "type": "boolean", + "description": "启用状态", + "name": "status", + "in": "query" + }, + { + "type": "integer", + "description": "关联标记", + "name": "sysDictionaryID", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "string", + "description": "字典值", + "name": "value", + "in": "query" + } + ], + "responses": { + "200": { + "description": "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/updateSysDictionaryDetail": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "更新SysDictionaryDetail", + "parameters": [ + { + "description": "更新SysDictionaryDetail", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + } + ], + "responses": { + "200": { + "description": "更新SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysExportTemplate/createSysExportTemplate": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "创建导出模板", + "parameters": [ + { + "description": "创建导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysExportTemplate" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/deleteSysExportTemplate": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "删除导出模板", + "parameters": [ + { + "description": "删除导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysExportTemplate" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/deleteSysExportTemplateByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "批量删除导出模板", + "parameters": [ + { + "description": "批量删除导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"批量删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/exportExcel": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "导出表格模板", + "responses": {} + } + }, + "/sysExportTemplate/findSysExportTemplate": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "用id查询导出模板", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "数据库名称", + "name": "dbName", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "模板名称", + "name": "name", + "in": "query" + }, + { + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "string", + "description": "表名称", + "name": "tableName", + "in": "query" + }, + { + "type": "string", + "description": "模板标识", + "name": "templateID", + "in": "query" + }, + { + "type": "string", + "description": "模板信息", + "name": "templateInfo", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/getSysExportTemplateList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "分页获取导出模板列表", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "数据库名称", + "name": "dbName", + "in": "query" + }, + { + "type": "string", + "name": "endCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "模板名称", + "name": "name", + "in": "query" + }, + { + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "name": "startCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "表名称", + "name": "tableName", + "in": "query" + }, + { + "type": "string", + "description": "模板标识", + "name": "templateID", + "in": "query" + }, + { + "type": "string", + "description": "模板信息", + "name": "templateInfo", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/importExcel": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysImportTemplate" + ], + "summary": "导入表格", + "responses": {} + } + }, + "/sysExportTemplate/updateSysExportTemplate": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "更新导出模板", + "parameters": [ + { + "description": "更新导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysExportTemplate" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysOperationRecord/createSysOperationRecord": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "创建SysOperationRecord", + "parameters": [ + { + "description": "创建SysOperationRecord", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysOperationRecord" + } + } + ], + "responses": { + "200": { + "description": "创建SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/deleteSysOperationRecord": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "删除SysOperationRecord", + "parameters": [ + { + "description": "SysOperationRecord模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysOperationRecord" + } + } + ], + "responses": { + "200": { + "description": "删除SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/deleteSysOperationRecordByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "批量删除SysOperationRecord", + "parameters": [ + { + "description": "批量删除SysOperationRecord", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "批量删除SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/findSysOperationRecord": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "用id查询SysOperationRecord", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "代理", + "name": "agent", + "in": "query" + }, + { + "type": "string", + "description": "请求Body", + "name": "body", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "错误信息", + "name": "error_message", + "in": "query" + }, + { + "type": "string", + "description": "请求ip", + "name": "ip", + "in": "query" + }, + { + "type": "string", + "description": "延迟", + "name": "latency", + "in": "query" + }, + { + "type": "string", + "description": "请求方法", + "name": "method", + "in": "query" + }, + { + "type": "string", + "description": "请求路径", + "name": "path", + "in": "query" + }, + { + "type": "string", + "description": "响应Body", + "name": "resp", + "in": "query" + }, + { + "type": "integer", + "description": "请求状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "integer", + "description": "用户id", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "用id查询SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/getSysOperationRecordList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "分页获取SysOperationRecord列表", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "代理", + "name": "agent", + "in": "query" + }, + { + "type": "string", + "description": "请求Body", + "name": "body", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "错误信息", + "name": "error_message", + "in": "query" + }, + { + "type": "string", + "description": "请求ip", + "name": "ip", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "string", + "description": "延迟", + "name": "latency", + "in": "query" + }, + { + "type": "string", + "description": "请求方法", + "name": "method", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "请求路径", + "name": "path", + "in": "query" + }, + { + "type": "string", + "description": "响应Body", + "name": "resp", + "in": "query" + }, + { + "type": "integer", + "description": "请求状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "integer", + "description": "用户id", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/getServerInfo": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "获取服务器信息", + "responses": { + "200": { + "description": "获取服务器信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/getSystemConfig": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "获取配置文件内容", + "responses": { + "200": { + "description": "获取配置文件内容,返回包括系统配置", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysConfigResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/reloadSystem": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "重启系统", + "responses": { + "200": { + "description": "重启系统", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/setSystemConfig": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "设置配置文件内容", + "parameters": [ + { + "description": "设置配置文件内容", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.System" + } + } + ], + "responses": { + "200": { + "description": "设置配置文件内容", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/SetSelfInfo": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "设置用户信息", + "parameters": [ + { + "description": "ID, 用户名, 昵称, 头像链接", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysUser" + } + } + ], + "responses": { + "200": { + "description": "设置用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/admin_register": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "用户注册账号", + "parameters": [ + { + "description": "用户名, 昵称, 密码, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Register" + } + } + ], + "responses": { + "200": { + "description": "用户注册账号,返回包括用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysUserResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/changePassword": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "用户修改密码", + "parameters": [ + { + "description": "用户名, 原密码, 新密码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChangePasswordReq" + } + } + ], + "responses": { + "200": { + "description": "用户修改密码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/deleteUser": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "删除用户", + "parameters": [ + { + "description": "用户ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除用户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/getUserInfo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "获取用户信息", + "responses": { + "200": { + "description": "获取用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/getUserList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "分页获取用户列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页获取用户列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/resetPassword": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "重置用户密码", + "parameters": [ + { + "description": "ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysUser" + } + } + ], + "responses": { + "200": { + "description": "重置用户密码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/setUserAuthorities": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "设置用户权限", + "parameters": [ + { + "description": "用户UUID, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetUserAuthorities" + } + } + ], + "responses": { + "200": { + "description": "设置用户权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/setUserAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "更改用户权限", + "parameters": [ + { + "description": "用户UUID, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetUserAuth" + } + } + ], + "responses": { + "200": { + "description": "设置用户权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/setUserInfo": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "设置用户信息", + "parameters": [ + { + "description": "ID, 用户名, 昵称, 头像链接", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysUser" + } + } + ], + "responses": { + "200": { + "description": "设置用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + } + }, + "definitions": { + "config.AliyunOSS": { + "type": "object", + "properties": { + "access-key-id": { + "type": "string" + }, + "access-key-secret": { + "type": "string" + }, + "base-path": { + "type": "string" + }, + "bucket-name": { + "type": "string" + }, + "bucket-url": { + "type": "string" + }, + "endpoint": { + "type": "string" + } + } + }, + "config.Autocode": { + "type": "object", + "properties": { + "ai-path": { + "type": "string" + }, + "module": { + "type": "string" + }, + "root": { + "type": "string" + }, + "server": { + "type": "string" + }, + "web": { + "type": "string" + } + } + }, + "config.AwsS3": { + "type": "object", + "properties": { + "base-url": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "disable-ssl": { + "type": "boolean" + }, + "endpoint": { + "type": "string" + }, + "path-prefix": { + "type": "string" + }, + "region": { + "type": "string" + }, + "s3-force-path-style": { + "type": "boolean" + }, + "secret-id": { + "type": "string" + }, + "secret-key": { + "type": "string" + } + } + }, + "config.CORS": { + "type": "object", + "properties": { + "mode": { + "type": "string" + }, + "whitelist": { + "type": "array", + "items": { + "$ref": "#/definitions/config.CORSWhitelist" + } + } + } + }, + "config.CORSWhitelist": { + "type": "object", + "properties": { + "allow-credentials": { + "type": "boolean" + }, + "allow-headers": { + "type": "string" + }, + "allow-methods": { + "type": "string" + }, + "allow-origin": { + "type": "string" + }, + "expose-headers": { + "type": "string" + } + } + }, + "config.Captcha": { + "type": "object", + "properties": { + "img-height": { + "description": "验证码高度", + "type": "integer" + }, + "img-width": { + "description": "验证码宽度", + "type": "integer" + }, + "key-long": { + "description": "验证码长度", + "type": "integer" + }, + "open-captcha": { + "description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码", + "type": "integer" + }, + "open-captcha-timeout": { + "description": "防爆破验证码超时时间,单位:s(秒)", + "type": "integer" + } + } + }, + "config.CloudflareR2": { + "type": "object", + "properties": { + "access-key-id": { + "type": "string" + }, + "account-id": { + "type": "string" + }, + "base-url": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "path": { + "type": "string" + }, + "secret-access-key": { + "type": "string" + } + } + }, + "config.DiskList": { + "type": "object", + "properties": { + "mount-point": { + "type": "string" + } + } + }, + "config.Excel": { + "type": "object", + "properties": { + "dir": { + "type": "string" + } + } + }, + "config.HuaWeiObs": { + "type": "object", + "properties": { + "access-key": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "path": { + "type": "string" + }, + "secret-key": { + "type": "string" + } + } + }, + "config.JWT": { + "type": "object", + "properties": { + "buffer-time": { + "description": "缓冲时间", + "type": "string" + }, + "expires-time": { + "description": "过期时间", + "type": "string" + }, + "issuer": { + "description": "签发者", + "type": "string" + }, + "signing-key": { + "description": "jwt签名", + "type": "string" + } + } + }, + "config.Local": { + "type": "object", + "properties": { + "path": { + "description": "本地文件访问路径", + "type": "string" + }, + "store-path": { + "description": "本地文件存储路径", + "type": "string" + } + } + }, + "config.Mongo": { + "type": "object", + "properties": { + "auth-source": { + "description": "验证数据库", + "type": "string" + }, + "coll": { + "description": "collection name", + "type": "string" + }, + "connect-timeout-ms": { + "description": "连接超时时间", + "type": "integer" + }, + "database": { + "description": "database name", + "type": "string" + }, + "hosts": { + "description": "主机列表", + "type": "array", + "items": { + "$ref": "#/definitions/config.MongoHost" + } + }, + "is-zap": { + "description": "是否开启zap日志", + "type": "boolean" + }, + "max-pool-size": { + "description": "最大连接池", + "type": "integer" + }, + "min-pool-size": { + "description": "最小连接池", + "type": "integer" + }, + "options": { + "description": "mongodb options", + "type": "string" + }, + "password": { + "description": "密码", + "type": "string" + }, + "socket-timeout-ms": { + "description": "socket超时时间", + "type": "integer" + }, + "username": { + "description": "用户名", + "type": "string" + } + } + }, + "config.MongoHost": { + "type": "object", + "properties": { + "host": { + "description": "ip地址", + "type": "string" + }, + "port": { + "description": "端口", + "type": "string" + } + } + }, + "config.Mssql": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Mysql": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Oracle": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Pgsql": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Qiniu": { + "type": "object", + "properties": { + "access-key": { + "description": "秘钥AK", + "type": "string" + }, + "bucket": { + "description": "空间名称", + "type": "string" + }, + "img-path": { + "description": "CDN加速域名", + "type": "string" + }, + "secret-key": { + "description": "秘钥SK", + "type": "string" + }, + "use-cdn-domains": { + "description": "上传是否使用CDN上传加速", + "type": "boolean" + }, + "use-https": { + "description": "是否使用https", + "type": "boolean" + }, + "zone": { + "description": "存储区域", + "type": "string" + } + } + }, + "config.Redis": { + "type": "object", + "properties": { + "addr": { + "description": "服务器地址:端口", + "type": "string" + }, + "clusterAddrs": { + "description": "集群模式下的节点地址列表", + "type": "array", + "items": { + "type": "string" + } + }, + "db": { + "description": "单实例模式下redis的哪个数据库", + "type": "integer" + }, + "password": { + "description": "密码", + "type": "string" + }, + "useCluster": { + "description": "是否使用集群模式", + "type": "boolean" + } + } + }, + "config.Server": { + "type": "object", + "properties": { + "aliyun-oss": { + "$ref": "#/definitions/config.AliyunOSS" + }, + "autocode": { + "description": "auto", + "allOf": [ + { + "$ref": "#/definitions/config.Autocode" + } + ] + }, + "aws-s3": { + "$ref": "#/definitions/config.AwsS3" + }, + "captcha": { + "$ref": "#/definitions/config.Captcha" + }, + "cloudflare-r2": { + "$ref": "#/definitions/config.CloudflareR2" + }, + "cors": { + "description": "跨域配置", + "allOf": [ + { + "$ref": "#/definitions/config.CORS" + } + ] + }, + "db-list": { + "type": "array", + "items": { + "$ref": "#/definitions/config.SpecializedDB" + } + }, + "disk-list": { + "type": "array", + "items": { + "$ref": "#/definitions/config.DiskList" + } + }, + "email": { + "$ref": "#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email" + }, + "excel": { + "$ref": "#/definitions/config.Excel" + }, + "hua-wei-obs": { + "$ref": "#/definitions/config.HuaWeiObs" + }, + "jwt": { + "$ref": "#/definitions/config.JWT" + }, + "local": { + "description": "oss", + "allOf": [ + { + "$ref": "#/definitions/config.Local" + } + ] + }, + "mongo": { + "$ref": "#/definitions/config.Mongo" + }, + "mssql": { + "$ref": "#/definitions/config.Mssql" + }, + "mysql": { + "description": "gorm", + "allOf": [ + { + "$ref": "#/definitions/config.Mysql" + } + ] + }, + "oracle": { + "$ref": "#/definitions/config.Oracle" + }, + "pgsql": { + "$ref": "#/definitions/config.Pgsql" + }, + "qiniu": { + "$ref": "#/definitions/config.Qiniu" + }, + "redis": { + "$ref": "#/definitions/config.Redis" + }, + "sqlite": { + "$ref": "#/definitions/config.Sqlite" + }, + "system": { + "$ref": "#/definitions/config.System" + }, + "tencent-cos": { + "$ref": "#/definitions/config.TencentCOS" + }, + "zap": { + "$ref": "#/definitions/config.Zap" + } + } + }, + "config.SpecializedDB": { + "type": "object", + "properties": { + "alias-name": { + "type": "string" + }, + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "disable": { + "type": "boolean" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Sqlite": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.System": { + "type": "object", + "properties": { + "addr": { + "description": "端口值", + "type": "integer" + }, + "db-type": { + "description": "数据库类型:mysql(默认)|sqlite|sqlserver|postgresql", + "type": "string" + }, + "iplimit-count": { + "type": "integer" + }, + "iplimit-time": { + "type": "integer" + }, + "oss-type": { + "description": "Oss类型", + "type": "string" + }, + "router-prefix": { + "type": "string" + }, + "use-mongo": { + "description": "使用mongo", + "type": "boolean" + }, + "use-multipoint": { + "description": "多点登录拦截", + "type": "boolean" + }, + "use-redis": { + "description": "使用redis", + "type": "boolean" + } + } + }, + "config.TencentCOS": { + "type": "object", + "properties": { + "base-url": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "path-prefix": { + "type": "string" + }, + "region": { + "type": "string" + }, + "secret-id": { + "type": "string" + }, + "secret-key": { + "type": "string" + } + } + }, + "config.Zap": { + "type": "object", + "properties": { + "director": { + "description": "日志文件夹", + "type": "string" + }, + "encode-level": { + "description": "编码级", + "type": "string" + }, + "format": { + "description": "输出", + "type": "string" + }, + "level": { + "description": "级别", + "type": "string" + }, + "log-in-console": { + "description": "输出控制台", + "type": "boolean" + }, + "prefix": { + "description": "日志前缀", + "type": "string" + }, + "retention-day": { + "description": "日志保留天数", + "type": "integer" + }, + "show-line": { + "description": "显示行", + "type": "boolean" + }, + "stacktrace-key": { + "description": "栈名", + "type": "string" + } + } + }, + "example.ExaCustomer": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "customerName": { + "description": "客户名", + "type": "string" + }, + "customerPhoneData": { + "description": "客户手机号", + "type": "string" + }, + "sysUser": { + "description": "管理详情", + "allOf": [ + { + "$ref": "#/definitions/system.SysUser" + } + ] + }, + "sysUserAuthorityID": { + "description": "管理角色ID", + "type": "integer" + }, + "sysUserId": { + "description": "管理ID", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "example.ExaFile": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "chunkTotal": { + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileChunk": { + "type": "array", + "items": { + "$ref": "#/definitions/example.ExaFileChunk" + } + }, + "fileMd5": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "filePath": { + "type": "string" + }, + "isFinish": { + "type": "boolean" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "example.ExaFileChunk": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileID": { + "type": "integer" + }, + "fileChunkNumber": { + "type": "integer" + }, + "fileChunkPath": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "example.ExaFileUploadAndDownload": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "key": { + "description": "编号", + "type": "string" + }, + "name": { + "description": "文件名", + "type": "string" + }, + "tag": { + "description": "文件标签", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "url": { + "description": "文件地址", + "type": "string" + } + } + }, + "github_com_flipped-aurora_gin-vue-admin_server_config.Email": { + "type": "object", + "properties": { + "from": { + "description": "发件人 你自己要发邮件的邮箱", + "type": "string" + }, + "host": { + "description": "服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议", + "type": "string" + }, + "is-ssl": { + "description": "是否SSL 是否开启SSL", + "type": "boolean" + }, + "nickname": { + "description": "昵称 发件人昵称 通常为自己的邮箱", + "type": "string" + }, + "port": { + "description": "端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465", + "type": "integer" + }, + "secret": { + "description": "密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥", + "type": "string" + }, + "to": { + "description": "收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用", + "type": "string" + } + } + }, + "model.Info": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "attachments": { + "description": "附件", + "type": "array", + "items": { + "type": "object" + } + }, + "content": { + "description": "内容", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "title": { + "description": "标题", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "userID": { + "description": "作者", + "type": "integer" + } + } + }, + "request.AddMenuAuthorityInfo": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + }, + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + } + }, + "request.AutoCode": { + "type": "object" + }, + "request.CasbinInReceive": { + "type": "object", + "properties": { + "authorityId": { + "description": "权限id", + "type": "integer" + }, + "casbinInfos": { + "type": "array", + "items": { + "$ref": "#/definitions/request.CasbinInfo" + } + } + } + }, + "request.CasbinInfo": { + "type": "object", + "properties": { + "method": { + "description": "方法", + "type": "string" + }, + "path": { + "description": "路径", + "type": "string" + } + } + }, + "request.ChangePasswordReq": { + "type": "object", + "properties": { + "newPassword": { + "description": "新密码", + "type": "string" + }, + "password": { + "description": "密码", + "type": "string" + } + } + }, + "request.Empty": { + "type": "object" + }, + "request.GetAuthorityId": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + } + } + }, + "request.GetById": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "request.IdsReq": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "request.InitDB": { + "type": "object", + "required": [ + "adminPassword", + "dbName" + ], + "properties": { + "adminPassword": { + "type": "string" + }, + "dbName": { + "description": "数据库名", + "type": "string" + }, + "dbPath": { + "description": "sqlite数据库文件路径", + "type": "string" + }, + "dbType": { + "description": "数据库类型", + "type": "string" + }, + "host": { + "description": "服务器地址", + "type": "string" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "port": { + "description": "数据库连接端口", + "type": "string" + }, + "userName": { + "description": "数据库用户名", + "type": "string" + } + } + }, + "request.Login": { + "type": "object", + "properties": { + "captcha": { + "description": "验证码", + "type": "string" + }, + "captchaId": { + "description": "验证码ID", + "type": "string" + }, + "password": { + "description": "密码", + "type": "string" + }, + "username": { + "description": "用户名", + "type": "string" + } + } + }, + "request.PageInfo": { + "type": "object", + "properties": { + "keyword": { + "description": "关键字", + "type": "string" + }, + "page": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.Register": { + "type": "object", + "properties": { + "authorityId": { + "type": "string", + "example": "int 角色id" + }, + "authorityIds": { + "type": "string", + "example": "[]uint 角色id" + }, + "email": { + "type": "string", + "example": "电子邮箱" + }, + "enable": { + "type": "string", + "example": "int 是否启用" + }, + "headerImg": { + "type": "string", + "example": "头像链接" + }, + "nickName": { + "type": "string", + "example": "昵称" + }, + "passWord": { + "type": "string", + "example": "密码" + }, + "phone": { + "type": "string", + "example": "电话号码" + }, + "userName": { + "type": "string", + "example": "用户名" + } + } + }, + "request.SearchApiParams": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "apiGroup": { + "description": "api组", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "desc": { + "description": "排序方式:升序false(默认)|降序true", + "type": "boolean" + }, + "description": { + "description": "api中文描述", + "type": "string" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "method": { + "description": "方法:创建POST(默认)|查看GET|更新PUT|删除DELETE", + "type": "string" + }, + "orderKey": { + "description": "排序", + "type": "string" + }, + "page": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "path": { + "description": "api路径", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "request.SetUserAuth": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + } + } + }, + "request.SetUserAuthorities": { + "type": "object", + "properties": { + "authorityIds": { + "description": "角色ID", + "type": "array", + "items": { + "type": "integer" + } + }, + "id": { + "type": "integer" + } + } + }, + "request.SysAuthorityBtnReq": { + "type": "object", + "properties": { + "authorityId": { + "type": "integer" + }, + "menuID": { + "type": "integer" + }, + "selected": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "request.SysAutoCodePackageCreate": { + "type": "object", + "properties": { + "desc": { + "type": "string", + "example": "描述" + }, + "label": { + "type": "string", + "example": "展示名" + }, + "packageName": { + "type": "string", + "example": "包名" + }, + "template": { + "type": "string", + "example": "模版" + } + } + }, + "request.SysAutoHistoryRollBack": { + "type": "object", + "properties": { + "deleteApi": { + "description": "是否删除接口", + "type": "boolean" + }, + "deleteMenu": { + "description": "是否删除菜单", + "type": "boolean" + }, + "deleteTable": { + "description": "是否删除表", + "type": "boolean" + }, + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "response.Email": { + "type": "object", + "properties": { + "body": { + "description": "邮件内容", + "type": "string" + }, + "subject": { + "description": "邮件标题", + "type": "string" + }, + "to": { + "description": "邮件发送给谁", + "type": "string" + } + } + }, + "response.ExaCustomerResponse": { + "type": "object", + "properties": { + "customer": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + }, + "response.ExaFileResponse": { + "type": "object", + "properties": { + "file": { + "$ref": "#/definitions/example.ExaFileUploadAndDownload" + } + } + }, + "response.FilePathResponse": { + "type": "object", + "properties": { + "filePath": { + "type": "string" + } + } + }, + "response.FileResponse": { + "type": "object", + "properties": { + "file": { + "$ref": "#/definitions/example.ExaFile" + } + } + }, + "response.LoginResponse": { + "type": "object", + "properties": { + "expiresAt": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/system.SysUser" + } + } + }, + "response.PageResult": { + "type": "object", + "properties": { + "list": {}, + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "response.PolicyPathResponse": { + "type": "object", + "properties": { + "paths": { + "type": "array", + "items": { + "$ref": "#/definitions/request.CasbinInfo" + } + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + } + }, + "response.SysAPIListResponse": { + "type": "object", + "properties": { + "apis": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysApi" + } + } + } + }, + "response.SysAPIResponse": { + "type": "object", + "properties": { + "api": { + "$ref": "#/definitions/system.SysApi" + } + } + }, + "response.SysAuthorityBtnRes": { + "type": "object", + "properties": { + "selected": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "response.SysAuthorityCopyResponse": { + "type": "object", + "properties": { + "authority": { + "$ref": "#/definitions/system.SysAuthority" + }, + "oldAuthorityId": { + "description": "旧角色ID", + "type": "integer" + } + } + }, + "response.SysAuthorityResponse": { + "type": "object", + "properties": { + "authority": { + "$ref": "#/definitions/system.SysAuthority" + } + } + }, + "response.SysBaseMenuResponse": { + "type": "object", + "properties": { + "menu": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + }, + "response.SysBaseMenusResponse": { + "type": "object", + "properties": { + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + } + }, + "response.SysCaptchaResponse": { + "type": "object", + "properties": { + "captchaId": { + "type": "string" + }, + "captchaLength": { + "type": "integer" + }, + "openCaptcha": { + "type": "boolean" + }, + "picPath": { + "type": "string" + } + } + }, + "response.SysConfigResponse": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/config.Server" + } + } + }, + "response.SysMenusResponse": { + "type": "object", + "properties": { + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysMenu" + } + } + } + }, + "response.SysUserResponse": { + "type": "object", + "properties": { + "user": { + "$ref": "#/definitions/system.SysUser" + } + } + }, + "system.Condition": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "column": { + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "from": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "templateID": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.JoinTemplate": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "joins": { + "type": "string" + }, + "on": { + "type": "string" + }, + "table": { + "type": "string" + }, + "templateID": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.Meta": { + "type": "object", + "properties": { + "activeName": { + "type": "string" + }, + "closeTab": { + "description": "自动关闭tab", + "type": "boolean" + }, + "defaultMenu": { + "description": "是否是基础路由(开发中)", + "type": "boolean" + }, + "icon": { + "description": "菜单图标", + "type": "string" + }, + "keepAlive": { + "description": "是否缓存", + "type": "boolean" + }, + "title": { + "description": "菜单名", + "type": "string" + } + } + }, + "system.SysApi": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "apiGroup": { + "description": "api组", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "description": { + "description": "api中文描述", + "type": "string" + }, + "method": { + "description": "方法:创建POST(默认)|查看GET|更新PUT|删除DELETE", + "type": "string" + }, + "path": { + "description": "api路径", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysAuthority": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + }, + "authorityName": { + "description": "角色名", + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "dataAuthorityId": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "defaultRouter": { + "description": "默认菜单(默认dashboard)", + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + }, + "parentId": { + "description": "父角色ID", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysBaseMenu": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "authoritys": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + }, + "component": { + "description": "对应前端文件路径", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "hidden": { + "description": "是否在列表隐藏", + "type": "boolean" + }, + "menuBtn": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuBtn" + } + }, + "meta": { + "description": "附加属性", + "allOf": [ + { + "$ref": "#/definitions/system.Meta" + } + ] + }, + "name": { + "description": "路由name", + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuParameter" + } + }, + "parentId": { + "description": "父菜单ID", + "type": "integer" + }, + "path": { + "description": "路由path", + "type": "string" + }, + "sort": { + "description": "排序标记", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysBaseMenuBtn": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "desc": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sysBaseMenuID": { + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysBaseMenuParameter": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "key": { + "description": "地址栏携带参数的key", + "type": "string" + }, + "sysBaseMenuID": { + "type": "integer" + }, + "type": { + "description": "地址栏携带参数为params还是query", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "value": { + "description": "地址栏携带参数的值", + "type": "string" + } + } + }, + "system.SysDictionary": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "desc": { + "description": "描述", + "type": "string" + }, + "name": { + "description": "字典名(中)", + "type": "string" + }, + "status": { + "description": "状态", + "type": "boolean" + }, + "sysDictionaryDetails": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "type": { + "description": "字典名(英)", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysDictionaryDetail": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "extend": { + "description": "扩展值", + "type": "string" + }, + "label": { + "description": "展示值", + "type": "string" + }, + "sort": { + "description": "排序标记", + "type": "integer" + }, + "status": { + "description": "启用状态", + "type": "boolean" + }, + "sysDictionaryID": { + "description": "关联标记", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "value": { + "description": "字典值", + "type": "string" + } + } + }, + "system.SysExportTemplate": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Condition" + } + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "dbName": { + "description": "数据库名称", + "type": "string" + }, + "joinTemplate": { + "type": "array", + "items": { + "$ref": "#/definitions/system.JoinTemplate" + } + }, + "limit": { + "type": "integer" + }, + "name": { + "description": "模板名称", + "type": "string" + }, + "order": { + "type": "string" + }, + "tableName": { + "description": "表名称", + "type": "string" + }, + "templateID": { + "description": "模板标识", + "type": "string" + }, + "templateInfo": { + "description": "模板信息", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysMenu": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "authoritys": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "btns": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysMenu" + } + }, + "component": { + "description": "对应前端文件路径", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "hidden": { + "description": "是否在列表隐藏", + "type": "boolean" + }, + "menuBtn": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuBtn" + } + }, + "menuId": { + "type": "integer" + }, + "meta": { + "description": "附加属性", + "allOf": [ + { + "$ref": "#/definitions/system.Meta" + } + ] + }, + "name": { + "description": "路由name", + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuParameter" + } + }, + "parentId": { + "description": "父菜单ID", + "type": "integer" + }, + "path": { + "description": "路由path", + "type": "string" + }, + "sort": { + "description": "排序标记", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysOperationRecord": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "agent": { + "description": "代理", + "type": "string" + }, + "body": { + "description": "请求Body", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "error_message": { + "description": "错误信息", + "type": "string" + }, + "ip": { + "description": "请求ip", + "type": "string" + }, + "latency": { + "description": "延迟", + "type": "string" + }, + "method": { + "description": "请求方法", + "type": "string" + }, + "path": { + "description": "请求路径", + "type": "string" + }, + "resp": { + "description": "响应Body", + "type": "string" + }, + "status": { + "description": "请求状态", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "user": { + "$ref": "#/definitions/system.SysUser" + }, + "user_id": { + "description": "用户id", + "type": "integer" + } + } + }, + "system.SysUser": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "authorities": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "authority": { + "$ref": "#/definitions/system.SysAuthority" + }, + "authorityId": { + "description": "用户角色ID", + "type": "integer" + }, + "baseColor": { + "description": "基础颜色", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "email": { + "description": "用户邮箱", + "type": "string" + }, + "enable": { + "description": "用户是否被冻结 1正常 2冻结", + "type": "integer" + }, + "headerImg": { + "description": "用户头像", + "type": "string" + }, + "nickName": { + "description": "用户昵称", + "type": "string" + }, + "phone": { + "description": "用户手机号", + "type": "string" + }, + "sideMode": { + "description": "用户侧边主题", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "userName": { + "description": "用户登录名", + "type": "string" + }, + "uuid": { + "description": "用户UUID", + "type": "string" + } + } + }, + "system.System": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/config.Server" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "x-token", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "v2.7.6", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "Gin-Vue-Admin Swagger API接口文档", + Description: "使用gin+vue进行极速开发的全栈开发基础平台", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/swagger.json b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/swagger.json new file mode 100644 index 000000000..4cbbd6002 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/swagger.json @@ -0,0 +1,8078 @@ +{ + "swagger": "2.0", + "info": { + "description": "使用gin+vue进行极速开发的全栈开发基础平台", + "title": "Gin-Vue-Admin Swagger API接口文档", + "contact": {}, + "version": "v2.7.6" + }, + "paths": { + "/api/createApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "创建基础api", + "parameters": [ + { + "description": "api路径, api中文描述, api组, 方法", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysApi" + } + } + ], + "responses": { + "200": { + "description": "创建基础api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/deleteApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "删除api", + "parameters": [ + { + "description": "ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysApi" + } + } + ], + "responses": { + "200": { + "description": "删除api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/deleteApisByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "删除选中Api", + "parameters": [ + { + "description": "ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除选中Api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/enterSyncApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "确认同步API", + "responses": { + "200": { + "description": "确认同步API", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/freshCasbin": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "刷新casbin缓存", + "responses": { + "200": { + "description": "刷新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/getAllApis": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "获取所有的Api 不分页", + "responses": { + "200": { + "description": "获取所有的Api 不分页,返回包括api列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAPIListResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/getApiById": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "根据id获取api", + "parameters": [ + { + "description": "根据id获取api", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "根据id获取api,返回包括api详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAPIResponse" + } + } + } + ] + } + } + } + } + }, + "/api/getApiGroups": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "获取API分组", + "responses": { + "200": { + "description": "获取API分组", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/getApiList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "分页获取API列表", + "parameters": [ + { + "description": "分页获取API列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SearchApiParams" + } + } + ], + "responses": { + "200": { + "description": "分页获取API列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/ignoreApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "IgnoreApi" + ], + "summary": "忽略API", + "responses": { + "200": { + "description": "同步API", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/syncApi": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "同步API", + "responses": { + "200": { + "description": "同步API", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/api/updateApi": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "修改基础api", + "parameters": [ + { + "description": "api路径, api中文描述, api组, 方法", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysApi" + } + } + ], + "responses": { + "200": { + "description": "修改基础api", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/copyAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "拷贝角色", + "parameters": [ + { + "description": "旧角色id, 新权限id, 新权限名, 新父角色id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/response.SysAuthorityCopyResponse" + } + } + ], + "responses": { + "200": { + "description": "拷贝角色,返回包括系统角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/createAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "创建角色", + "parameters": [ + { + "description": "权限id, 权限名, 父角色id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "创建角色,返回包括系统角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/deleteAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "删除角色", + "parameters": [ + { + "description": "删除角色", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "删除角色", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/getAuthorityList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "分页获取角色列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页获取角色列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/setDataAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "设置角色资源权限", + "parameters": [ + { + "description": "设置角色资源权限", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "设置角色资源权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authority/updateAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "更新角色信息", + "parameters": [ + { + "description": "权限id, 权限名, 父角色id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysAuthority" + } + } + ], + "responses": { + "200": { + "description": "更新角色信息,返回包括系统角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authorityBtn/canRemoveAuthorityBtn": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityBtn" + ], + "summary": "设置权限按钮", + "responses": { + "200": { + "description": "删除成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authorityBtn/getAuthorityBtn": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityBtn" + ], + "summary": "获取权限按钮", + "parameters": [ + { + "description": "菜单id, 角色id, 选中的按钮id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAuthorityBtnReq" + } + } + ], + "responses": { + "200": { + "description": "返回列表成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysAuthorityBtnRes" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/authorityBtn/setAuthorityBtn": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityBtn" + ], + "summary": "设置权限按钮", + "parameters": [ + { + "description": "菜单id, 角色id, 选中的按钮id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAuthorityBtnReq" + } + } + ], + "responses": { + "200": { + "description": "返回列表成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/addFunc": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AddFunc" + ], + "summary": "增加方法", + "parameters": [ + { + "description": "增加方法", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AutoCode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/autoCode/createPackage": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePackage" + ], + "summary": "创建package", + "parameters": [ + { + "description": "创建package", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAutoCodePackageCreate" + } + } + ], + "responses": { + "200": { + "description": "创建package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/createTemp": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodeTemplate" + ], + "summary": "自动代码模板", + "parameters": [ + { + "description": "创建自动代码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AutoCode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/autoCode/delPackage": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "删除package", + "parameters": [ + { + "description": "创建package", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/delSysHistory": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "删除回滚记录", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除回滚记录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getColumn": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取当前表所有字段", + "responses": { + "200": { + "description": "获取当前表所有字段", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getDatabase": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取当前所有数据库", + "responses": { + "200": { + "description": "获取当前所有数据库", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getMeta": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取meta信息", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "获取meta信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getPackage": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePackage" + ], + "summary": "获取package", + "responses": { + "200": { + "description": "创建package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getSysHistory": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "查询回滚记录", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "查询回滚记录,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getTables": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "获取当前数据库所有表", + "responses": { + "200": { + "description": "获取当前数据库所有表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/getTemplates": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePackage" + ], + "summary": "获取package", + "responses": { + "200": { + "description": "创建package成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/installPlugin": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePlugin" + ], + "summary": "安装插件", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "plug", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "安装插件成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/preview": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodeTemplate" + ], + "summary": "预览创建后的代码", + "parameters": [ + { + "description": "预览创建代码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AutoCode" + } + } + ], + "responses": { + "200": { + "description": "预览创建后的代码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/pubPlug": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCodePlugin" + ], + "summary": "打包插件", + "parameters": [ + { + "type": "string", + "description": "插件名称", + "name": "plugName", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "打包插件成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/autoCode/rollback": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AutoCode" + ], + "summary": "回滚自动生成代码", + "parameters": [ + { + "description": "请求参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SysAutoHistoryRollBack" + } + } + ], + "responses": { + "200": { + "description": "回滚自动生成代码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/base/captcha": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "生成验证码", + "responses": { + "200": { + "description": "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysCaptchaResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/base/login": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "用户名, 密码, 验证码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Login" + } + } + ], + "responses": { + "200": { + "description": "返回包括用户信息,token,过期时间", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.LoginResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/casbin/UpdateCasbin": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Casbin" + ], + "summary": "更新角色api权限", + "parameters": [ + { + "description": "权限id, 权限模型列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CasbinInReceive" + } + } + ], + "responses": { + "200": { + "description": "更新角色api权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/casbin/getPolicyPathByAuthorityId": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Casbin" + ], + "summary": "获取权限列表", + "parameters": [ + { + "description": "权限id, 权限模型列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CasbinInReceive" + } + } + ], + "responses": { + "200": { + "description": "获取权限列表,返回包括casbin详情列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PolicyPathResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/customer/customer": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "获取单一客户信息", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "客户名", + "name": "customerName", + "in": "query" + }, + { + "type": "string", + "description": "客户手机号", + "name": "customerPhoneData", + "in": "query" + }, + { + "type": "integer", + "description": "管理角色ID", + "name": "sysUserAuthorityID", + "in": "query" + }, + { + "type": "integer", + "description": "管理ID", + "name": "sysUserId", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取单一客户信息,返回包括客户详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.ExaCustomerResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "更新客户信息", + "parameters": [ + { + "description": "客户ID, 客户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + ], + "responses": { + "200": { + "description": "更新客户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "创建客户", + "parameters": [ + { + "description": "客户用户名, 客户手机号码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + ], + "responses": { + "200": { + "description": "创建客户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "删除客户", + "parameters": [ + { + "description": "客户ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + ], + "responses": { + "200": { + "description": "删除客户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/customer/customerList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaCustomer" + ], + "summary": "分页获取权限客户列表", + "parameters": [ + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "分页获取权限客户列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/email/emailTest": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "发送测试邮件", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/email/sendEmail": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "发送邮件", + "parameters": [ + { + "description": "发送邮件必须的参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/response.Email" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/fileUploadAndDownload/breakpointContinue": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "断点续传到服务器", + "parameters": [ + { + "type": "file", + "description": "an example for breakpoint resume, 断点续传示例", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "断点续传到服务器", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/deleteFile": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "删除文件", + "parameters": [ + { + "description": "传入文件里面id即可", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/example.ExaFileUploadAndDownload" + } + } + ], + "responses": { + "200": { + "description": "删除文件", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/findFile": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "创建文件", + "parameters": [ + { + "type": "file", + "description": "上传文件完成", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "创建文件,返回包括文件路径", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.FilePathResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/getFileList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "分页文件列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页文件列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/removeChunk": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "删除切片", + "parameters": [ + { + "type": "file", + "description": "删除缓存切片", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "删除切片", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/fileUploadAndDownload/upload": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ExaFileUploadAndDownload" + ], + "summary": "上传文件示例", + "parameters": [ + { + "type": "file", + "description": "上传文件示例", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "上传文件示例,返回包括文件详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.ExaFileResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/createInfo": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "创建公告", + "parameters": [ + { + "description": "创建公告", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Info" + } + } + ], + "responses": { + "200": { + "description": "创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/deleteInfo": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "删除公告", + "parameters": [ + { + "description": "删除公告", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Info" + } + } + ], + "responses": { + "200": { + "description": "删除成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/deleteInfoByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "批量删除公告", + "responses": { + "200": { + "description": "批量删除成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/findInfo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "用id查询公告", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "内容", + "name": "content", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "标题", + "name": "title", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "integer", + "description": "作者", + "name": "userID", + "in": "query" + } + ], + "responses": { + "200": { + "description": "查询成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Info" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/getInfoDataSource": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "获取Info的数据源", + "responses": { + "200": { + "description": "查询成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/getInfoList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "分页获取公告列表", + "parameters": [ + { + "type": "string", + "name": "endCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "name": "startCreatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/getInfoPublic": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "不需要鉴权的公告接口", + "parameters": [ + { + "type": "string", + "name": "endCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "name": "startCreatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/info/updateInfo": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Info" + ], + "summary": "更新公告", + "parameters": [ + { + "description": "更新公告", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Info" + } + } + ], + "responses": { + "200": { + "description": "更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/init/checkdb": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "CheckDB" + ], + "summary": "初始化用户数据库", + "responses": { + "200": { + "description": "初始化用户数据库", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/init/initdb": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "InitDB" + ], + "summary": "初始化用户数据库", + "parameters": [ + { + "description": "初始化数据库参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.InitDB" + } + } + ], + "responses": { + "200": { + "description": "初始化用户数据库", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/jwt/jsonInBlacklist": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Jwt" + ], + "summary": "jwt加入黑名单", + "responses": { + "200": { + "description": "jwt加入黑名单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/addBaseMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "新增菜单", + "parameters": [ + { + "description": "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + ], + "responses": { + "200": { + "description": "新增菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/addMenuAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "增加menu和角色关联关系", + "parameters": [ + { + "description": "角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AddMenuAuthorityInfo" + } + } + ], + "responses": { + "200": { + "description": "增加menu和角色关联关系", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/deleteBaseMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "删除菜单", + "parameters": [ + { + "description": "菜单id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getBaseMenuById": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "根据id获取菜单", + "parameters": [ + { + "description": "菜单id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "根据id获取菜单,返回包括系统菜单列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysBaseMenuResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getBaseMenuTree": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取用户动态路由", + "parameters": [ + { + "description": "空", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Empty" + } + } + ], + "responses": { + "200": { + "description": "获取用户动态路由,返回包括系统菜单列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysBaseMenusResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取用户动态路由", + "parameters": [ + { + "description": "空", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Empty" + } + } + ], + "responses": { + "200": { + "description": "获取用户动态路由,返回包括系统菜单详情列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysMenusResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getMenuAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取指定角色menu", + "parameters": [ + { + "description": "角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetAuthorityId" + } + } + ], + "responses": { + "200": { + "description": "获取指定角色menu", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getMenuList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "分页获取基础menu列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页获取基础menu列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/updateBaseMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Menu" + ], + "summary": "更新菜单", + "parameters": [ + { + "description": "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + ], + "responses": { + "200": { + "description": "更新菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/createSysDictionary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "创建SysDictionary", + "parameters": [ + { + "description": "SysDictionary模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionary" + } + } + ], + "responses": { + "200": { + "description": "创建SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/deleteSysDictionary": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "删除SysDictionary", + "parameters": [ + { + "description": "SysDictionary模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionary" + } + } + ], + "responses": { + "200": { + "description": "删除SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/findSysDictionary": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "用id查询SysDictionary", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "描述", + "name": "desc", + "in": "query" + }, + { + "type": "string", + "description": "字典名(中)", + "name": "name", + "in": "query" + }, + { + "type": "boolean", + "description": "状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "字典名(英)", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "用id查询SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/getSysDictionaryList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "分页获取SysDictionary列表", + "responses": { + "200": { + "description": "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionary/updateSysDictionary": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "更新SysDictionary", + "parameters": [ + { + "description": "SysDictionary模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionary" + } + } + ], + "responses": { + "200": { + "description": "更新SysDictionary", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/createSysDictionaryDetail": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "创建SysDictionaryDetail", + "parameters": [ + { + "description": "SysDictionaryDetail模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + } + ], + "responses": { + "200": { + "description": "创建SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/deleteSysDictionaryDetail": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "删除SysDictionaryDetail", + "parameters": [ + { + "description": "SysDictionaryDetail模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + } + ], + "responses": { + "200": { + "description": "删除SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/findSysDictionaryDetail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "用id查询SysDictionaryDetail", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "扩展值", + "name": "extend", + "in": "query" + }, + { + "type": "string", + "description": "展示值", + "name": "label", + "in": "query" + }, + { + "type": "integer", + "description": "排序标记", + "name": "sort", + "in": "query" + }, + { + "type": "boolean", + "description": "启用状态", + "name": "status", + "in": "query" + }, + { + "type": "integer", + "description": "关联标记", + "name": "sysDictionaryID", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "string", + "description": "字典值", + "name": "value", + "in": "query" + } + ], + "responses": { + "200": { + "description": "用id查询SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getSysDictionaryDetailList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "分页获取SysDictionaryDetail列表", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "扩展值", + "name": "extend", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "string", + "description": "展示值", + "name": "label", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "integer", + "description": "排序标记", + "name": "sort", + "in": "query" + }, + { + "type": "boolean", + "description": "启用状态", + "name": "status", + "in": "query" + }, + { + "type": "integer", + "description": "关联标记", + "name": "sysDictionaryID", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "string", + "description": "字典值", + "name": "value", + "in": "query" + } + ], + "responses": { + "200": { + "description": "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/updateSysDictionaryDetail": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "更新SysDictionaryDetail", + "parameters": [ + { + "description": "更新SysDictionaryDetail", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + } + ], + "responses": { + "200": { + "description": "更新SysDictionaryDetail", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysExportTemplate/createSysExportTemplate": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "创建导出模板", + "parameters": [ + { + "description": "创建导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysExportTemplate" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/deleteSysExportTemplate": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "删除导出模板", + "parameters": [ + { + "description": "删除导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysExportTemplate" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/deleteSysExportTemplateByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "批量删除导出模板", + "parameters": [ + { + "description": "批量删除导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"批量删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/exportExcel": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "导出表格模板", + "responses": {} + } + }, + "/sysExportTemplate/findSysExportTemplate": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "用id查询导出模板", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "数据库名称", + "name": "dbName", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "模板名称", + "name": "name", + "in": "query" + }, + { + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "string", + "description": "表名称", + "name": "tableName", + "in": "query" + }, + { + "type": "string", + "description": "模板标识", + "name": "templateID", + "in": "query" + }, + { + "type": "string", + "description": "模板信息", + "name": "templateInfo", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/getSysExportTemplateList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "分页获取导出模板列表", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "数据库名称", + "name": "dbName", + "in": "query" + }, + { + "type": "string", + "name": "endCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "模板名称", + "name": "name", + "in": "query" + }, + { + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "name": "startCreatedAt", + "in": "query" + }, + { + "type": "string", + "description": "表名称", + "name": "tableName", + "in": "query" + }, + { + "type": "string", + "description": "模板标识", + "name": "templateID", + "in": "query" + }, + { + "type": "string", + "description": "模板信息", + "name": "templateInfo", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysExportTemplate/importExcel": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysImportTemplate" + ], + "summary": "导入表格", + "responses": {} + } + }, + "/sysExportTemplate/updateSysExportTemplate": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysExportTemplate" + ], + "summary": "更新导出模板", + "parameters": [ + { + "description": "更新导出模板", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysExportTemplate" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/sysOperationRecord/createSysOperationRecord": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "创建SysOperationRecord", + "parameters": [ + { + "description": "创建SysOperationRecord", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysOperationRecord" + } + } + ], + "responses": { + "200": { + "description": "创建SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/deleteSysOperationRecord": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "删除SysOperationRecord", + "parameters": [ + { + "description": "SysOperationRecord模型", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysOperationRecord" + } + } + ], + "responses": { + "200": { + "description": "删除SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/deleteSysOperationRecordByIds": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "批量删除SysOperationRecord", + "parameters": [ + { + "description": "批量删除SysOperationRecord", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "批量删除SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/findSysOperationRecord": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "用id查询SysOperationRecord", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "代理", + "name": "agent", + "in": "query" + }, + { + "type": "string", + "description": "请求Body", + "name": "body", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "错误信息", + "name": "error_message", + "in": "query" + }, + { + "type": "string", + "description": "请求ip", + "name": "ip", + "in": "query" + }, + { + "type": "string", + "description": "延迟", + "name": "latency", + "in": "query" + }, + { + "type": "string", + "description": "请求方法", + "name": "method", + "in": "query" + }, + { + "type": "string", + "description": "请求路径", + "name": "path", + "in": "query" + }, + { + "type": "string", + "description": "响应Body", + "name": "resp", + "in": "query" + }, + { + "type": "integer", + "description": "请求状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "integer", + "description": "用户id", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "用id查询SysOperationRecord", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysOperationRecord/getSysOperationRecordList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysOperationRecord" + ], + "summary": "分页获取SysOperationRecord列表", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "代理", + "name": "agent", + "in": "query" + }, + { + "type": "string", + "description": "请求Body", + "name": "body", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "错误信息", + "name": "error_message", + "in": "query" + }, + { + "type": "string", + "description": "请求ip", + "name": "ip", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "string", + "description": "延迟", + "name": "latency", + "in": "query" + }, + { + "type": "string", + "description": "请求方法", + "name": "method", + "in": "query" + }, + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "请求路径", + "name": "path", + "in": "query" + }, + { + "type": "string", + "description": "响应Body", + "name": "resp", + "in": "query" + }, + { + "type": "integer", + "description": "请求状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + }, + { + "type": "integer", + "description": "用户id", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/getServerInfo": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "获取服务器信息", + "responses": { + "200": { + "description": "获取服务器信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/getSystemConfig": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "获取配置文件内容", + "responses": { + "200": { + "description": "获取配置文件内容,返回包括系统配置", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysConfigResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/reloadSystem": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "重启系统", + "responses": { + "200": { + "description": "重启系统", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/system/setSystemConfig": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "设置配置文件内容", + "parameters": [ + { + "description": "设置配置文件内容", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.System" + } + } + ], + "responses": { + "200": { + "description": "设置配置文件内容", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/SetSelfInfo": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "设置用户信息", + "parameters": [ + { + "description": "ID, 用户名, 昵称, 头像链接", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysUser" + } + } + ], + "responses": { + "200": { + "description": "设置用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/admin_register": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "用户注册账号", + "parameters": [ + { + "description": "用户名, 昵称, 密码, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Register" + } + } + ], + "responses": { + "200": { + "description": "用户注册账号,返回包括用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.SysUserResponse" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/changePassword": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "用户修改密码", + "parameters": [ + { + "description": "用户名, 原密码, 新密码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChangePasswordReq" + } + } + ], + "responses": { + "200": { + "description": "用户修改密码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/deleteUser": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "删除用户", + "parameters": [ + { + "description": "用户ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetById" + } + } + ], + "responses": { + "200": { + "description": "删除用户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/getUserInfo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "获取用户信息", + "responses": { + "200": { + "description": "获取用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/getUserList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "分页获取用户列表", + "parameters": [ + { + "description": "页码, 每页大小", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "分页获取用户列表,返回包括列表,总数,页码,每页数量", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/resetPassword": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "重置用户密码", + "parameters": [ + { + "description": "ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysUser" + } + } + ], + "responses": { + "200": { + "description": "重置用户密码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/setUserAuthorities": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "设置用户权限", + "parameters": [ + { + "description": "用户UUID, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetUserAuthorities" + } + } + ], + "responses": { + "200": { + "description": "设置用户权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/setUserAuthority": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "更改用户权限", + "parameters": [ + { + "description": "用户UUID, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetUserAuth" + } + } + ], + "responses": { + "200": { + "description": "设置用户权限", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/setUserInfo": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysUser" + ], + "summary": "设置用户信息", + "parameters": [ + { + "description": "ID, 用户名, 昵称, 头像链接", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.SysUser" + } + } + ], + "responses": { + "200": { + "description": "设置用户信息", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + } + }, + "definitions": { + "config.AliyunOSS": { + "type": "object", + "properties": { + "access-key-id": { + "type": "string" + }, + "access-key-secret": { + "type": "string" + }, + "base-path": { + "type": "string" + }, + "bucket-name": { + "type": "string" + }, + "bucket-url": { + "type": "string" + }, + "endpoint": { + "type": "string" + } + } + }, + "config.Autocode": { + "type": "object", + "properties": { + "ai-path": { + "type": "string" + }, + "module": { + "type": "string" + }, + "root": { + "type": "string" + }, + "server": { + "type": "string" + }, + "web": { + "type": "string" + } + } + }, + "config.AwsS3": { + "type": "object", + "properties": { + "base-url": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "disable-ssl": { + "type": "boolean" + }, + "endpoint": { + "type": "string" + }, + "path-prefix": { + "type": "string" + }, + "region": { + "type": "string" + }, + "s3-force-path-style": { + "type": "boolean" + }, + "secret-id": { + "type": "string" + }, + "secret-key": { + "type": "string" + } + } + }, + "config.CORS": { + "type": "object", + "properties": { + "mode": { + "type": "string" + }, + "whitelist": { + "type": "array", + "items": { + "$ref": "#/definitions/config.CORSWhitelist" + } + } + } + }, + "config.CORSWhitelist": { + "type": "object", + "properties": { + "allow-credentials": { + "type": "boolean" + }, + "allow-headers": { + "type": "string" + }, + "allow-methods": { + "type": "string" + }, + "allow-origin": { + "type": "string" + }, + "expose-headers": { + "type": "string" + } + } + }, + "config.Captcha": { + "type": "object", + "properties": { + "img-height": { + "description": "验证码高度", + "type": "integer" + }, + "img-width": { + "description": "验证码宽度", + "type": "integer" + }, + "key-long": { + "description": "验证码长度", + "type": "integer" + }, + "open-captcha": { + "description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码", + "type": "integer" + }, + "open-captcha-timeout": { + "description": "防爆破验证码超时时间,单位:s(秒)", + "type": "integer" + } + } + }, + "config.CloudflareR2": { + "type": "object", + "properties": { + "access-key-id": { + "type": "string" + }, + "account-id": { + "type": "string" + }, + "base-url": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "path": { + "type": "string" + }, + "secret-access-key": { + "type": "string" + } + } + }, + "config.DiskList": { + "type": "object", + "properties": { + "mount-point": { + "type": "string" + } + } + }, + "config.Excel": { + "type": "object", + "properties": { + "dir": { + "type": "string" + } + } + }, + "config.HuaWeiObs": { + "type": "object", + "properties": { + "access-key": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "path": { + "type": "string" + }, + "secret-key": { + "type": "string" + } + } + }, + "config.JWT": { + "type": "object", + "properties": { + "buffer-time": { + "description": "缓冲时间", + "type": "string" + }, + "expires-time": { + "description": "过期时间", + "type": "string" + }, + "issuer": { + "description": "签发者", + "type": "string" + }, + "signing-key": { + "description": "jwt签名", + "type": "string" + } + } + }, + "config.Local": { + "type": "object", + "properties": { + "path": { + "description": "本地文件访问路径", + "type": "string" + }, + "store-path": { + "description": "本地文件存储路径", + "type": "string" + } + } + }, + "config.Mongo": { + "type": "object", + "properties": { + "auth-source": { + "description": "验证数据库", + "type": "string" + }, + "coll": { + "description": "collection name", + "type": "string" + }, + "connect-timeout-ms": { + "description": "连接超时时间", + "type": "integer" + }, + "database": { + "description": "database name", + "type": "string" + }, + "hosts": { + "description": "主机列表", + "type": "array", + "items": { + "$ref": "#/definitions/config.MongoHost" + } + }, + "is-zap": { + "description": "是否开启zap日志", + "type": "boolean" + }, + "max-pool-size": { + "description": "最大连接池", + "type": "integer" + }, + "min-pool-size": { + "description": "最小连接池", + "type": "integer" + }, + "options": { + "description": "mongodb options", + "type": "string" + }, + "password": { + "description": "密码", + "type": "string" + }, + "socket-timeout-ms": { + "description": "socket超时时间", + "type": "integer" + }, + "username": { + "description": "用户名", + "type": "string" + } + } + }, + "config.MongoHost": { + "type": "object", + "properties": { + "host": { + "description": "ip地址", + "type": "string" + }, + "port": { + "description": "端口", + "type": "string" + } + } + }, + "config.Mssql": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Mysql": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Oracle": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Pgsql": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Qiniu": { + "type": "object", + "properties": { + "access-key": { + "description": "秘钥AK", + "type": "string" + }, + "bucket": { + "description": "空间名称", + "type": "string" + }, + "img-path": { + "description": "CDN加速域名", + "type": "string" + }, + "secret-key": { + "description": "秘钥SK", + "type": "string" + }, + "use-cdn-domains": { + "description": "上传是否使用CDN上传加速", + "type": "boolean" + }, + "use-https": { + "description": "是否使用https", + "type": "boolean" + }, + "zone": { + "description": "存储区域", + "type": "string" + } + } + }, + "config.Redis": { + "type": "object", + "properties": { + "addr": { + "description": "服务器地址:端口", + "type": "string" + }, + "clusterAddrs": { + "description": "集群模式下的节点地址列表", + "type": "array", + "items": { + "type": "string" + } + }, + "db": { + "description": "单实例模式下redis的哪个数据库", + "type": "integer" + }, + "password": { + "description": "密码", + "type": "string" + }, + "useCluster": { + "description": "是否使用集群模式", + "type": "boolean" + } + } + }, + "config.Server": { + "type": "object", + "properties": { + "aliyun-oss": { + "$ref": "#/definitions/config.AliyunOSS" + }, + "autocode": { + "description": "auto", + "allOf": [ + { + "$ref": "#/definitions/config.Autocode" + } + ] + }, + "aws-s3": { + "$ref": "#/definitions/config.AwsS3" + }, + "captcha": { + "$ref": "#/definitions/config.Captcha" + }, + "cloudflare-r2": { + "$ref": "#/definitions/config.CloudflareR2" + }, + "cors": { + "description": "跨域配置", + "allOf": [ + { + "$ref": "#/definitions/config.CORS" + } + ] + }, + "db-list": { + "type": "array", + "items": { + "$ref": "#/definitions/config.SpecializedDB" + } + }, + "disk-list": { + "type": "array", + "items": { + "$ref": "#/definitions/config.DiskList" + } + }, + "email": { + "$ref": "#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email" + }, + "excel": { + "$ref": "#/definitions/config.Excel" + }, + "hua-wei-obs": { + "$ref": "#/definitions/config.HuaWeiObs" + }, + "jwt": { + "$ref": "#/definitions/config.JWT" + }, + "local": { + "description": "oss", + "allOf": [ + { + "$ref": "#/definitions/config.Local" + } + ] + }, + "mongo": { + "$ref": "#/definitions/config.Mongo" + }, + "mssql": { + "$ref": "#/definitions/config.Mssql" + }, + "mysql": { + "description": "gorm", + "allOf": [ + { + "$ref": "#/definitions/config.Mysql" + } + ] + }, + "oracle": { + "$ref": "#/definitions/config.Oracle" + }, + "pgsql": { + "$ref": "#/definitions/config.Pgsql" + }, + "qiniu": { + "$ref": "#/definitions/config.Qiniu" + }, + "redis": { + "$ref": "#/definitions/config.Redis" + }, + "sqlite": { + "$ref": "#/definitions/config.Sqlite" + }, + "system": { + "$ref": "#/definitions/config.System" + }, + "tencent-cos": { + "$ref": "#/definitions/config.TencentCOS" + }, + "zap": { + "$ref": "#/definitions/config.Zap" + } + } + }, + "config.SpecializedDB": { + "type": "object", + "properties": { + "alias-name": { + "type": "string" + }, + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "disable": { + "type": "boolean" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.Sqlite": { + "type": "object", + "properties": { + "config": { + "description": "高级配置", + "type": "string" + }, + "db-name": { + "description": "数据库名", + "type": "string" + }, + "engine": { + "description": "数据库引擎,默认InnoDB", + "type": "string", + "default": "InnoDB" + }, + "log-mode": { + "description": "是否开启Gorm全局日志", + "type": "string" + }, + "log-zap": { + "description": "是否通过zap写入日志文件", + "type": "boolean" + }, + "max-idle-conns": { + "description": "空闲中的最大连接数", + "type": "integer" + }, + "max-open-conns": { + "description": "打开到数据库的最大连接数", + "type": "integer" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "path": { + "description": "数据库地址", + "type": "string" + }, + "port": { + "description": "数据库端口", + "type": "string" + }, + "prefix": { + "description": "数据库前缀", + "type": "string" + }, + "singular": { + "description": "是否开启全局禁用复数,true表示开启", + "type": "boolean" + }, + "username": { + "description": "数据库账号", + "type": "string" + } + } + }, + "config.System": { + "type": "object", + "properties": { + "addr": { + "description": "端口值", + "type": "integer" + }, + "db-type": { + "description": "数据库类型:mysql(默认)|sqlite|sqlserver|postgresql", + "type": "string" + }, + "iplimit-count": { + "type": "integer" + }, + "iplimit-time": { + "type": "integer" + }, + "oss-type": { + "description": "Oss类型", + "type": "string" + }, + "router-prefix": { + "type": "string" + }, + "use-mongo": { + "description": "使用mongo", + "type": "boolean" + }, + "use-multipoint": { + "description": "多点登录拦截", + "type": "boolean" + }, + "use-redis": { + "description": "使用redis", + "type": "boolean" + } + } + }, + "config.TencentCOS": { + "type": "object", + "properties": { + "base-url": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "path-prefix": { + "type": "string" + }, + "region": { + "type": "string" + }, + "secret-id": { + "type": "string" + }, + "secret-key": { + "type": "string" + } + } + }, + "config.Zap": { + "type": "object", + "properties": { + "director": { + "description": "日志文件夹", + "type": "string" + }, + "encode-level": { + "description": "编码级", + "type": "string" + }, + "format": { + "description": "输出", + "type": "string" + }, + "level": { + "description": "级别", + "type": "string" + }, + "log-in-console": { + "description": "输出控制台", + "type": "boolean" + }, + "prefix": { + "description": "日志前缀", + "type": "string" + }, + "retention-day": { + "description": "日志保留天数", + "type": "integer" + }, + "show-line": { + "description": "显示行", + "type": "boolean" + }, + "stacktrace-key": { + "description": "栈名", + "type": "string" + } + } + }, + "example.ExaCustomer": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "customerName": { + "description": "客户名", + "type": "string" + }, + "customerPhoneData": { + "description": "客户手机号", + "type": "string" + }, + "sysUser": { + "description": "管理详情", + "allOf": [ + { + "$ref": "#/definitions/system.SysUser" + } + ] + }, + "sysUserAuthorityID": { + "description": "管理角色ID", + "type": "integer" + }, + "sysUserId": { + "description": "管理ID", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "example.ExaFile": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "chunkTotal": { + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileChunk": { + "type": "array", + "items": { + "$ref": "#/definitions/example.ExaFileChunk" + } + }, + "fileMd5": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "filePath": { + "type": "string" + }, + "isFinish": { + "type": "boolean" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "example.ExaFileChunk": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileID": { + "type": "integer" + }, + "fileChunkNumber": { + "type": "integer" + }, + "fileChunkPath": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "example.ExaFileUploadAndDownload": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "key": { + "description": "编号", + "type": "string" + }, + "name": { + "description": "文件名", + "type": "string" + }, + "tag": { + "description": "文件标签", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "url": { + "description": "文件地址", + "type": "string" + } + } + }, + "github_com_flipped-aurora_gin-vue-admin_server_config.Email": { + "type": "object", + "properties": { + "from": { + "description": "发件人 你自己要发邮件的邮箱", + "type": "string" + }, + "host": { + "description": "服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议", + "type": "string" + }, + "is-ssl": { + "description": "是否SSL 是否开启SSL", + "type": "boolean" + }, + "nickname": { + "description": "昵称 发件人昵称 通常为自己的邮箱", + "type": "string" + }, + "port": { + "description": "端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465", + "type": "integer" + }, + "secret": { + "description": "密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥", + "type": "string" + }, + "to": { + "description": "收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用", + "type": "string" + } + } + }, + "model.Info": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "attachments": { + "description": "附件", + "type": "array", + "items": { + "type": "object" + } + }, + "content": { + "description": "内容", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "title": { + "description": "标题", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "userID": { + "description": "作者", + "type": "integer" + } + } + }, + "request.AddMenuAuthorityInfo": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + }, + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + } + }, + "request.AutoCode": { + "type": "object" + }, + "request.CasbinInReceive": { + "type": "object", + "properties": { + "authorityId": { + "description": "权限id", + "type": "integer" + }, + "casbinInfos": { + "type": "array", + "items": { + "$ref": "#/definitions/request.CasbinInfo" + } + } + } + }, + "request.CasbinInfo": { + "type": "object", + "properties": { + "method": { + "description": "方法", + "type": "string" + }, + "path": { + "description": "路径", + "type": "string" + } + } + }, + "request.ChangePasswordReq": { + "type": "object", + "properties": { + "newPassword": { + "description": "新密码", + "type": "string" + }, + "password": { + "description": "密码", + "type": "string" + } + } + }, + "request.Empty": { + "type": "object" + }, + "request.GetAuthorityId": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + } + } + }, + "request.GetById": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "request.IdsReq": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "request.InitDB": { + "type": "object", + "required": [ + "adminPassword", + "dbName" + ], + "properties": { + "adminPassword": { + "type": "string" + }, + "dbName": { + "description": "数据库名", + "type": "string" + }, + "dbPath": { + "description": "sqlite数据库文件路径", + "type": "string" + }, + "dbType": { + "description": "数据库类型", + "type": "string" + }, + "host": { + "description": "服务器地址", + "type": "string" + }, + "password": { + "description": "数据库密码", + "type": "string" + }, + "port": { + "description": "数据库连接端口", + "type": "string" + }, + "userName": { + "description": "数据库用户名", + "type": "string" + } + } + }, + "request.Login": { + "type": "object", + "properties": { + "captcha": { + "description": "验证码", + "type": "string" + }, + "captchaId": { + "description": "验证码ID", + "type": "string" + }, + "password": { + "description": "密码", + "type": "string" + }, + "username": { + "description": "用户名", + "type": "string" + } + } + }, + "request.PageInfo": { + "type": "object", + "properties": { + "keyword": { + "description": "关键字", + "type": "string" + }, + "page": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.Register": { + "type": "object", + "properties": { + "authorityId": { + "type": "string", + "example": "int 角色id" + }, + "authorityIds": { + "type": "string", + "example": "[]uint 角色id" + }, + "email": { + "type": "string", + "example": "电子邮箱" + }, + "enable": { + "type": "string", + "example": "int 是否启用" + }, + "headerImg": { + "type": "string", + "example": "头像链接" + }, + "nickName": { + "type": "string", + "example": "昵称" + }, + "passWord": { + "type": "string", + "example": "密码" + }, + "phone": { + "type": "string", + "example": "电话号码" + }, + "userName": { + "type": "string", + "example": "用户名" + } + } + }, + "request.SearchApiParams": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "apiGroup": { + "description": "api组", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "desc": { + "description": "排序方式:升序false(默认)|降序true", + "type": "boolean" + }, + "description": { + "description": "api中文描述", + "type": "string" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "method": { + "description": "方法:创建POST(默认)|查看GET|更新PUT|删除DELETE", + "type": "string" + }, + "orderKey": { + "description": "排序", + "type": "string" + }, + "page": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "path": { + "description": "api路径", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "request.SetUserAuth": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + } + } + }, + "request.SetUserAuthorities": { + "type": "object", + "properties": { + "authorityIds": { + "description": "角色ID", + "type": "array", + "items": { + "type": "integer" + } + }, + "id": { + "type": "integer" + } + } + }, + "request.SysAuthorityBtnReq": { + "type": "object", + "properties": { + "authorityId": { + "type": "integer" + }, + "menuID": { + "type": "integer" + }, + "selected": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "request.SysAutoCodePackageCreate": { + "type": "object", + "properties": { + "desc": { + "type": "string", + "example": "描述" + }, + "label": { + "type": "string", + "example": "展示名" + }, + "packageName": { + "type": "string", + "example": "包名" + }, + "template": { + "type": "string", + "example": "模版" + } + } + }, + "request.SysAutoHistoryRollBack": { + "type": "object", + "properties": { + "deleteApi": { + "description": "是否删除接口", + "type": "boolean" + }, + "deleteMenu": { + "description": "是否删除菜单", + "type": "boolean" + }, + "deleteTable": { + "description": "是否删除表", + "type": "boolean" + }, + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "response.Email": { + "type": "object", + "properties": { + "body": { + "description": "邮件内容", + "type": "string" + }, + "subject": { + "description": "邮件标题", + "type": "string" + }, + "to": { + "description": "邮件发送给谁", + "type": "string" + } + } + }, + "response.ExaCustomerResponse": { + "type": "object", + "properties": { + "customer": { + "$ref": "#/definitions/example.ExaCustomer" + } + } + }, + "response.ExaFileResponse": { + "type": "object", + "properties": { + "file": { + "$ref": "#/definitions/example.ExaFileUploadAndDownload" + } + } + }, + "response.FilePathResponse": { + "type": "object", + "properties": { + "filePath": { + "type": "string" + } + } + }, + "response.FileResponse": { + "type": "object", + "properties": { + "file": { + "$ref": "#/definitions/example.ExaFile" + } + } + }, + "response.LoginResponse": { + "type": "object", + "properties": { + "expiresAt": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/system.SysUser" + } + } + }, + "response.PageResult": { + "type": "object", + "properties": { + "list": {}, + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "response.PolicyPathResponse": { + "type": "object", + "properties": { + "paths": { + "type": "array", + "items": { + "$ref": "#/definitions/request.CasbinInfo" + } + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + } + }, + "response.SysAPIListResponse": { + "type": "object", + "properties": { + "apis": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysApi" + } + } + } + }, + "response.SysAPIResponse": { + "type": "object", + "properties": { + "api": { + "$ref": "#/definitions/system.SysApi" + } + } + }, + "response.SysAuthorityBtnRes": { + "type": "object", + "properties": { + "selected": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "response.SysAuthorityCopyResponse": { + "type": "object", + "properties": { + "authority": { + "$ref": "#/definitions/system.SysAuthority" + }, + "oldAuthorityId": { + "description": "旧角色ID", + "type": "integer" + } + } + }, + "response.SysAuthorityResponse": { + "type": "object", + "properties": { + "authority": { + "$ref": "#/definitions/system.SysAuthority" + } + } + }, + "response.SysBaseMenuResponse": { + "type": "object", + "properties": { + "menu": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + }, + "response.SysBaseMenusResponse": { + "type": "object", + "properties": { + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + } + } + }, + "response.SysCaptchaResponse": { + "type": "object", + "properties": { + "captchaId": { + "type": "string" + }, + "captchaLength": { + "type": "integer" + }, + "openCaptcha": { + "type": "boolean" + }, + "picPath": { + "type": "string" + } + } + }, + "response.SysConfigResponse": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/config.Server" + } + } + }, + "response.SysMenusResponse": { + "type": "object", + "properties": { + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysMenu" + } + } + } + }, + "response.SysUserResponse": { + "type": "object", + "properties": { + "user": { + "$ref": "#/definitions/system.SysUser" + } + } + }, + "system.Condition": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "column": { + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "from": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "templateID": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.JoinTemplate": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "joins": { + "type": "string" + }, + "on": { + "type": "string" + }, + "table": { + "type": "string" + }, + "templateID": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.Meta": { + "type": "object", + "properties": { + "activeName": { + "type": "string" + }, + "closeTab": { + "description": "自动关闭tab", + "type": "boolean" + }, + "defaultMenu": { + "description": "是否是基础路由(开发中)", + "type": "boolean" + }, + "icon": { + "description": "菜单图标", + "type": "string" + }, + "keepAlive": { + "description": "是否缓存", + "type": "boolean" + }, + "title": { + "description": "菜单名", + "type": "string" + } + } + }, + "system.SysApi": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "apiGroup": { + "description": "api组", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "description": { + "description": "api中文描述", + "type": "string" + }, + "method": { + "description": "方法:创建POST(默认)|查看GET|更新PUT|删除DELETE", + "type": "string" + }, + "path": { + "description": "api路径", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysAuthority": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + }, + "authorityName": { + "description": "角色名", + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "dataAuthorityId": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "defaultRouter": { + "description": "默认菜单(默认dashboard)", + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + }, + "parentId": { + "description": "父角色ID", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysBaseMenu": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "authoritys": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenu" + } + }, + "component": { + "description": "对应前端文件路径", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "hidden": { + "description": "是否在列表隐藏", + "type": "boolean" + }, + "menuBtn": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuBtn" + } + }, + "meta": { + "description": "附加属性", + "allOf": [ + { + "$ref": "#/definitions/system.Meta" + } + ] + }, + "name": { + "description": "路由name", + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuParameter" + } + }, + "parentId": { + "description": "父菜单ID", + "type": "integer" + }, + "path": { + "description": "路由path", + "type": "string" + }, + "sort": { + "description": "排序标记", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysBaseMenuBtn": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "desc": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sysBaseMenuID": { + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysBaseMenuParameter": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "key": { + "description": "地址栏携带参数的key", + "type": "string" + }, + "sysBaseMenuID": { + "type": "integer" + }, + "type": { + "description": "地址栏携带参数为params还是query", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "value": { + "description": "地址栏携带参数的值", + "type": "string" + } + } + }, + "system.SysDictionary": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "desc": { + "description": "描述", + "type": "string" + }, + "name": { + "description": "字典名(中)", + "type": "string" + }, + "status": { + "description": "状态", + "type": "boolean" + }, + "sysDictionaryDetails": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "type": { + "description": "字典名(英)", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysDictionaryDetail": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "extend": { + "description": "扩展值", + "type": "string" + }, + "label": { + "description": "展示值", + "type": "string" + }, + "sort": { + "description": "排序标记", + "type": "integer" + }, + "status": { + "description": "启用状态", + "type": "boolean" + }, + "sysDictionaryID": { + "description": "关联标记", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "value": { + "description": "字典值", + "type": "string" + } + } + }, + "system.SysExportTemplate": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Condition" + } + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "dbName": { + "description": "数据库名称", + "type": "string" + }, + "joinTemplate": { + "type": "array", + "items": { + "$ref": "#/definitions/system.JoinTemplate" + } + }, + "limit": { + "type": "integer" + }, + "name": { + "description": "模板名称", + "type": "string" + }, + "order": { + "type": "string" + }, + "tableName": { + "description": "表名称", + "type": "string" + }, + "templateID": { + "description": "模板标识", + "type": "string" + }, + "templateInfo": { + "description": "模板信息", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysMenu": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "authoritys": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "btns": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysMenu" + } + }, + "component": { + "description": "对应前端文件路径", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "hidden": { + "description": "是否在列表隐藏", + "type": "boolean" + }, + "menuBtn": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuBtn" + } + }, + "menuId": { + "type": "integer" + }, + "meta": { + "description": "附加属性", + "allOf": [ + { + "$ref": "#/definitions/system.Meta" + } + ] + }, + "name": { + "description": "路由name", + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysBaseMenuParameter" + } + }, + "parentId": { + "description": "父菜单ID", + "type": "integer" + }, + "path": { + "description": "路由path", + "type": "string" + }, + "sort": { + "description": "排序标记", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "system.SysOperationRecord": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "agent": { + "description": "代理", + "type": "string" + }, + "body": { + "description": "请求Body", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "error_message": { + "description": "错误信息", + "type": "string" + }, + "ip": { + "description": "请求ip", + "type": "string" + }, + "latency": { + "description": "延迟", + "type": "string" + }, + "method": { + "description": "请求方法", + "type": "string" + }, + "path": { + "description": "请求路径", + "type": "string" + }, + "resp": { + "description": "响应Body", + "type": "string" + }, + "status": { + "description": "请求状态", + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "user": { + "$ref": "#/definitions/system.SysUser" + }, + "user_id": { + "description": "用户id", + "type": "integer" + } + } + }, + "system.SysUser": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "authorities": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysAuthority" + } + }, + "authority": { + "$ref": "#/definitions/system.SysAuthority" + }, + "authorityId": { + "description": "用户角色ID", + "type": "integer" + }, + "baseColor": { + "description": "基础颜色", + "type": "string" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "email": { + "description": "用户邮箱", + "type": "string" + }, + "enable": { + "description": "用户是否被冻结 1正常 2冻结", + "type": "integer" + }, + "headerImg": { + "description": "用户头像", + "type": "string" + }, + "nickName": { + "description": "用户昵称", + "type": "string" + }, + "phone": { + "description": "用户手机号", + "type": "string" + }, + "sideMode": { + "description": "用户侧边主题", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "userName": { + "description": "用户登录名", + "type": "string" + }, + "uuid": { + "description": "用户UUID", + "type": "string" + } + } + }, + "system.System": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/config.Server" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "x-token", + "in": "header" + } + } +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/swagger.yaml b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/swagger.yaml new file mode 100644 index 000000000..2a75c7df1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/docs/swagger.yaml @@ -0,0 +1,4927 @@ +definitions: + config.AliyunOSS: + properties: + access-key-id: + type: string + access-key-secret: + type: string + base-path: + type: string + bucket-name: + type: string + bucket-url: + type: string + endpoint: + type: string + type: object + config.Autocode: + properties: + ai-path: + type: string + module: + type: string + root: + type: string + server: + type: string + web: + type: string + type: object + config.AwsS3: + properties: + base-url: + type: string + bucket: + type: string + disable-ssl: + type: boolean + endpoint: + type: string + path-prefix: + type: string + region: + type: string + s3-force-path-style: + type: boolean + secret-id: + type: string + secret-key: + type: string + type: object + config.CORS: + properties: + mode: + type: string + whitelist: + items: + $ref: '#/definitions/config.CORSWhitelist' + type: array + type: object + config.CORSWhitelist: + properties: + allow-credentials: + type: boolean + allow-headers: + type: string + allow-methods: + type: string + allow-origin: + type: string + expose-headers: + type: string + type: object + config.Captcha: + properties: + img-height: + description: 验证码高度 + type: integer + img-width: + description: 验证码宽度 + type: integer + key-long: + description: 验证码长度 + type: integer + open-captcha: + description: 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码 + type: integer + open-captcha-timeout: + description: 防爆破验证码超时时间,单位:s(秒) + type: integer + type: object + config.CloudflareR2: + properties: + access-key-id: + type: string + account-id: + type: string + base-url: + type: string + bucket: + type: string + path: + type: string + secret-access-key: + type: string + type: object + config.DiskList: + properties: + mount-point: + type: string + type: object + config.Excel: + properties: + dir: + type: string + type: object + config.HuaWeiObs: + properties: + access-key: + type: string + bucket: + type: string + endpoint: + type: string + path: + type: string + secret-key: + type: string + type: object + config.JWT: + properties: + buffer-time: + description: 缓冲时间 + type: string + expires-time: + description: 过期时间 + type: string + issuer: + description: 签发者 + type: string + signing-key: + description: jwt签名 + type: string + type: object + config.Local: + properties: + path: + description: 本地文件访问路径 + type: string + store-path: + description: 本地文件存储路径 + type: string + type: object + config.Mongo: + properties: + auth-source: + description: 验证数据库 + type: string + coll: + description: collection name + type: string + connect-timeout-ms: + description: 连接超时时间 + type: integer + database: + description: database name + type: string + hosts: + description: 主机列表 + items: + $ref: '#/definitions/config.MongoHost' + type: array + is-zap: + description: 是否开启zap日志 + type: boolean + max-pool-size: + description: 最大连接池 + type: integer + min-pool-size: + description: 最小连接池 + type: integer + options: + description: mongodb options + type: string + password: + description: 密码 + type: string + socket-timeout-ms: + description: socket超时时间 + type: integer + username: + description: 用户名 + type: string + type: object + config.MongoHost: + properties: + host: + description: ip地址 + type: string + port: + description: 端口 + type: string + type: object + config.Mssql: + properties: + config: + description: 高级配置 + type: string + db-name: + description: 数据库名 + type: string + engine: + default: InnoDB + description: 数据库引擎,默认InnoDB + type: string + log-mode: + description: 是否开启Gorm全局日志 + type: string + log-zap: + description: 是否通过zap写入日志文件 + type: boolean + max-idle-conns: + description: 空闲中的最大连接数 + type: integer + max-open-conns: + description: 打开到数据库的最大连接数 + type: integer + password: + description: 数据库密码 + type: string + path: + description: 数据库地址 + type: string + port: + description: 数据库端口 + type: string + prefix: + description: 数据库前缀 + type: string + singular: + description: 是否开启全局禁用复数,true表示开启 + type: boolean + username: + description: 数据库账号 + type: string + type: object + config.Mysql: + properties: + config: + description: 高级配置 + type: string + db-name: + description: 数据库名 + type: string + engine: + default: InnoDB + description: 数据库引擎,默认InnoDB + type: string + log-mode: + description: 是否开启Gorm全局日志 + type: string + log-zap: + description: 是否通过zap写入日志文件 + type: boolean + max-idle-conns: + description: 空闲中的最大连接数 + type: integer + max-open-conns: + description: 打开到数据库的最大连接数 + type: integer + password: + description: 数据库密码 + type: string + path: + description: 数据库地址 + type: string + port: + description: 数据库端口 + type: string + prefix: + description: 数据库前缀 + type: string + singular: + description: 是否开启全局禁用复数,true表示开启 + type: boolean + username: + description: 数据库账号 + type: string + type: object + config.Oracle: + properties: + config: + description: 高级配置 + type: string + db-name: + description: 数据库名 + type: string + engine: + default: InnoDB + description: 数据库引擎,默认InnoDB + type: string + log-mode: + description: 是否开启Gorm全局日志 + type: string + log-zap: + description: 是否通过zap写入日志文件 + type: boolean + max-idle-conns: + description: 空闲中的最大连接数 + type: integer + max-open-conns: + description: 打开到数据库的最大连接数 + type: integer + password: + description: 数据库密码 + type: string + path: + description: 数据库地址 + type: string + port: + description: 数据库端口 + type: string + prefix: + description: 数据库前缀 + type: string + singular: + description: 是否开启全局禁用复数,true表示开启 + type: boolean + username: + description: 数据库账号 + type: string + type: object + config.Pgsql: + properties: + config: + description: 高级配置 + type: string + db-name: + description: 数据库名 + type: string + engine: + default: InnoDB + description: 数据库引擎,默认InnoDB + type: string + log-mode: + description: 是否开启Gorm全局日志 + type: string + log-zap: + description: 是否通过zap写入日志文件 + type: boolean + max-idle-conns: + description: 空闲中的最大连接数 + type: integer + max-open-conns: + description: 打开到数据库的最大连接数 + type: integer + password: + description: 数据库密码 + type: string + path: + description: 数据库地址 + type: string + port: + description: 数据库端口 + type: string + prefix: + description: 数据库前缀 + type: string + singular: + description: 是否开启全局禁用复数,true表示开启 + type: boolean + username: + description: 数据库账号 + type: string + type: object + config.Qiniu: + properties: + access-key: + description: 秘钥AK + type: string + bucket: + description: 空间名称 + type: string + img-path: + description: CDN加速域名 + type: string + secret-key: + description: 秘钥SK + type: string + use-cdn-domains: + description: 上传是否使用CDN上传加速 + type: boolean + use-https: + description: 是否使用https + type: boolean + zone: + description: 存储区域 + type: string + type: object + config.Redis: + properties: + addr: + description: 服务器地址:端口 + type: string + clusterAddrs: + description: 集群模式下的节点地址列表 + items: + type: string + type: array + db: + description: 单实例模式下redis的哪个数据库 + type: integer + password: + description: 密码 + type: string + useCluster: + description: 是否使用集群模式 + type: boolean + type: object + config.Server: + properties: + aliyun-oss: + $ref: '#/definitions/config.AliyunOSS' + autocode: + allOf: + - $ref: '#/definitions/config.Autocode' + description: auto + aws-s3: + $ref: '#/definitions/config.AwsS3' + captcha: + $ref: '#/definitions/config.Captcha' + cloudflare-r2: + $ref: '#/definitions/config.CloudflareR2' + cors: + allOf: + - $ref: '#/definitions/config.CORS' + description: 跨域配置 + db-list: + items: + $ref: '#/definitions/config.SpecializedDB' + type: array + disk-list: + items: + $ref: '#/definitions/config.DiskList' + type: array + email: + $ref: '#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email' + excel: + $ref: '#/definitions/config.Excel' + hua-wei-obs: + $ref: '#/definitions/config.HuaWeiObs' + jwt: + $ref: '#/definitions/config.JWT' + local: + allOf: + - $ref: '#/definitions/config.Local' + description: oss + mongo: + $ref: '#/definitions/config.Mongo' + mssql: + $ref: '#/definitions/config.Mssql' + mysql: + allOf: + - $ref: '#/definitions/config.Mysql' + description: gorm + oracle: + $ref: '#/definitions/config.Oracle' + pgsql: + $ref: '#/definitions/config.Pgsql' + qiniu: + $ref: '#/definitions/config.Qiniu' + redis: + $ref: '#/definitions/config.Redis' + sqlite: + $ref: '#/definitions/config.Sqlite' + system: + $ref: '#/definitions/config.System' + tencent-cos: + $ref: '#/definitions/config.TencentCOS' + zap: + $ref: '#/definitions/config.Zap' + type: object + config.SpecializedDB: + properties: + alias-name: + type: string + config: + description: 高级配置 + type: string + db-name: + description: 数据库名 + type: string + disable: + type: boolean + engine: + default: InnoDB + description: 数据库引擎,默认InnoDB + type: string + log-mode: + description: 是否开启Gorm全局日志 + type: string + log-zap: + description: 是否通过zap写入日志文件 + type: boolean + max-idle-conns: + description: 空闲中的最大连接数 + type: integer + max-open-conns: + description: 打开到数据库的最大连接数 + type: integer + password: + description: 数据库密码 + type: string + path: + description: 数据库地址 + type: string + port: + description: 数据库端口 + type: string + prefix: + description: 数据库前缀 + type: string + singular: + description: 是否开启全局禁用复数,true表示开启 + type: boolean + type: + type: string + username: + description: 数据库账号 + type: string + type: object + config.Sqlite: + properties: + config: + description: 高级配置 + type: string + db-name: + description: 数据库名 + type: string + engine: + default: InnoDB + description: 数据库引擎,默认InnoDB + type: string + log-mode: + description: 是否开启Gorm全局日志 + type: string + log-zap: + description: 是否通过zap写入日志文件 + type: boolean + max-idle-conns: + description: 空闲中的最大连接数 + type: integer + max-open-conns: + description: 打开到数据库的最大连接数 + type: integer + password: + description: 数据库密码 + type: string + path: + description: 数据库地址 + type: string + port: + description: 数据库端口 + type: string + prefix: + description: 数据库前缀 + type: string + singular: + description: 是否开启全局禁用复数,true表示开启 + type: boolean + username: + description: 数据库账号 + type: string + type: object + config.System: + properties: + addr: + description: 端口值 + type: integer + db-type: + description: 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql + type: string + iplimit-count: + type: integer + iplimit-time: + type: integer + oss-type: + description: Oss类型 + type: string + router-prefix: + type: string + use-mongo: + description: 使用mongo + type: boolean + use-multipoint: + description: 多点登录拦截 + type: boolean + use-redis: + description: 使用redis + type: boolean + type: object + config.TencentCOS: + properties: + base-url: + type: string + bucket: + type: string + path-prefix: + type: string + region: + type: string + secret-id: + type: string + secret-key: + type: string + type: object + config.Zap: + properties: + director: + description: 日志文件夹 + type: string + encode-level: + description: 编码级 + type: string + format: + description: 输出 + type: string + level: + description: 级别 + type: string + log-in-console: + description: 输出控制台 + type: boolean + prefix: + description: 日志前缀 + type: string + retention-day: + description: 日志保留天数 + type: integer + show-line: + description: 显示行 + type: boolean + stacktrace-key: + description: 栈名 + type: string + type: object + example.ExaCustomer: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + customerName: + description: 客户名 + type: string + customerPhoneData: + description: 客户手机号 + type: string + sysUser: + allOf: + - $ref: '#/definitions/system.SysUser' + description: 管理详情 + sysUserAuthorityID: + description: 管理角色ID + type: integer + sysUserId: + description: 管理ID + type: integer + updatedAt: + description: 更新时间 + type: string + type: object + example.ExaFile: + properties: + ID: + description: 主键ID + type: integer + chunkTotal: + type: integer + createdAt: + description: 创建时间 + type: string + exaFileChunk: + items: + $ref: '#/definitions/example.ExaFileChunk' + type: array + fileMd5: + type: string + fileName: + type: string + filePath: + type: string + isFinish: + type: boolean + updatedAt: + description: 更新时间 + type: string + type: object + example.ExaFileChunk: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + exaFileID: + type: integer + fileChunkNumber: + type: integer + fileChunkPath: + type: string + updatedAt: + description: 更新时间 + type: string + type: object + example.ExaFileUploadAndDownload: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + key: + description: 编号 + type: string + name: + description: 文件名 + type: string + tag: + description: 文件标签 + type: string + updatedAt: + description: 更新时间 + type: string + url: + description: 文件地址 + type: string + type: object + github_com_flipped-aurora_gin-vue-admin_server_config.Email: + properties: + from: + description: 发件人 你自己要发邮件的邮箱 + type: string + host: + description: 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 + type: string + is-ssl: + description: 是否SSL 是否开启SSL + type: boolean + nickname: + description: 昵称 发件人昵称 通常为自己的邮箱 + type: string + port: + description: 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 + type: integer + secret: + description: 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 + type: string + to: + description: 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 + type: string + type: object + model.Info: + properties: + ID: + description: 主键ID + type: integer + attachments: + description: 附件 + items: + type: object + type: array + content: + description: 内容 + type: string + createdAt: + description: 创建时间 + type: string + title: + description: 标题 + type: string + updatedAt: + description: 更新时间 + type: string + userID: + description: 作者 + type: integer + type: object + request.AddMenuAuthorityInfo: + properties: + authorityId: + description: 角色ID + type: integer + menus: + items: + $ref: '#/definitions/system.SysBaseMenu' + type: array + type: object + request.AutoCode: + type: object + request.CasbinInReceive: + properties: + authorityId: + description: 权限id + type: integer + casbinInfos: + items: + $ref: '#/definitions/request.CasbinInfo' + type: array + type: object + request.CasbinInfo: + properties: + method: + description: 方法 + type: string + path: + description: 路径 + type: string + type: object + request.ChangePasswordReq: + properties: + newPassword: + description: 新密码 + type: string + password: + description: 密码 + type: string + type: object + request.Empty: + type: object + request.GetAuthorityId: + properties: + authorityId: + description: 角色ID + type: integer + type: object + request.GetById: + properties: + id: + description: 主键ID + type: integer + type: object + request.IdsReq: + properties: + ids: + items: + type: integer + type: array + type: object + request.InitDB: + properties: + adminPassword: + type: string + dbName: + description: 数据库名 + type: string + dbPath: + description: sqlite数据库文件路径 + type: string + dbType: + description: 数据库类型 + type: string + host: + description: 服务器地址 + type: string + password: + description: 数据库密码 + type: string + port: + description: 数据库连接端口 + type: string + userName: + description: 数据库用户名 + type: string + required: + - adminPassword + - dbName + type: object + request.Login: + properties: + captcha: + description: 验证码 + type: string + captchaId: + description: 验证码ID + type: string + password: + description: 密码 + type: string + username: + description: 用户名 + type: string + type: object + request.PageInfo: + properties: + keyword: + description: 关键字 + type: string + page: + description: 页码 + type: integer + pageSize: + description: 每页大小 + type: integer + type: object + request.Register: + properties: + authorityId: + example: int 角色id + type: string + authorityIds: + example: '[]uint 角色id' + type: string + email: + example: 电子邮箱 + type: string + enable: + example: int 是否启用 + type: string + headerImg: + example: 头像链接 + type: string + nickName: + example: 昵称 + type: string + passWord: + example: 密码 + type: string + phone: + example: 电话号码 + type: string + userName: + example: 用户名 + type: string + type: object + request.SearchApiParams: + properties: + ID: + description: 主键ID + type: integer + apiGroup: + description: api组 + type: string + createdAt: + description: 创建时间 + type: string + desc: + description: 排序方式:升序false(默认)|降序true + type: boolean + description: + description: api中文描述 + type: string + keyword: + description: 关键字 + type: string + method: + description: 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE + type: string + orderKey: + description: 排序 + type: string + page: + description: 页码 + type: integer + pageSize: + description: 每页大小 + type: integer + path: + description: api路径 + type: string + updatedAt: + description: 更新时间 + type: string + type: object + request.SetUserAuth: + properties: + authorityId: + description: 角色ID + type: integer + type: object + request.SetUserAuthorities: + properties: + authorityIds: + description: 角色ID + items: + type: integer + type: array + id: + type: integer + type: object + request.SysAuthorityBtnReq: + properties: + authorityId: + type: integer + menuID: + type: integer + selected: + items: + type: integer + type: array + type: object + request.SysAutoCodePackageCreate: + properties: + desc: + example: 描述 + type: string + label: + example: 展示名 + type: string + packageName: + example: 包名 + type: string + template: + example: 模版 + type: string + type: object + request.SysAutoHistoryRollBack: + properties: + deleteApi: + description: 是否删除接口 + type: boolean + deleteMenu: + description: 是否删除菜单 + type: boolean + deleteTable: + description: 是否删除表 + type: boolean + id: + description: 主键ID + type: integer + type: object + response.Email: + properties: + body: + description: 邮件内容 + type: string + subject: + description: 邮件标题 + type: string + to: + description: 邮件发送给谁 + type: string + type: object + response.ExaCustomerResponse: + properties: + customer: + $ref: '#/definitions/example.ExaCustomer' + type: object + response.ExaFileResponse: + properties: + file: + $ref: '#/definitions/example.ExaFileUploadAndDownload' + type: object + response.FilePathResponse: + properties: + filePath: + type: string + type: object + response.FileResponse: + properties: + file: + $ref: '#/definitions/example.ExaFile' + type: object + response.LoginResponse: + properties: + expiresAt: + type: integer + token: + type: string + user: + $ref: '#/definitions/system.SysUser' + type: object + response.PageResult: + properties: + list: {} + page: + type: integer + pageSize: + type: integer + total: + type: integer + type: object + response.PolicyPathResponse: + properties: + paths: + items: + $ref: '#/definitions/request.CasbinInfo' + type: array + type: object + response.Response: + properties: + code: + type: integer + data: {} + msg: + type: string + type: object + response.SysAPIListResponse: + properties: + apis: + items: + $ref: '#/definitions/system.SysApi' + type: array + type: object + response.SysAPIResponse: + properties: + api: + $ref: '#/definitions/system.SysApi' + type: object + response.SysAuthorityBtnRes: + properties: + selected: + items: + type: integer + type: array + type: object + response.SysAuthorityCopyResponse: + properties: + authority: + $ref: '#/definitions/system.SysAuthority' + oldAuthorityId: + description: 旧角色ID + type: integer + type: object + response.SysAuthorityResponse: + properties: + authority: + $ref: '#/definitions/system.SysAuthority' + type: object + response.SysBaseMenuResponse: + properties: + menu: + $ref: '#/definitions/system.SysBaseMenu' + type: object + response.SysBaseMenusResponse: + properties: + menus: + items: + $ref: '#/definitions/system.SysBaseMenu' + type: array + type: object + response.SysCaptchaResponse: + properties: + captchaId: + type: string + captchaLength: + type: integer + openCaptcha: + type: boolean + picPath: + type: string + type: object + response.SysConfigResponse: + properties: + config: + $ref: '#/definitions/config.Server' + type: object + response.SysMenusResponse: + properties: + menus: + items: + $ref: '#/definitions/system.SysMenu' + type: array + type: object + response.SysUserResponse: + properties: + user: + $ref: '#/definitions/system.SysUser' + type: object + system.Condition: + properties: + ID: + description: 主键ID + type: integer + column: + type: string + createdAt: + description: 创建时间 + type: string + from: + type: string + operator: + type: string + templateID: + type: string + updatedAt: + description: 更新时间 + type: string + type: object + system.JoinTemplate: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + joins: + type: string + "on": + type: string + table: + type: string + templateID: + type: string + updatedAt: + description: 更新时间 + type: string + type: object + system.Meta: + properties: + activeName: + type: string + closeTab: + description: 自动关闭tab + type: boolean + defaultMenu: + description: 是否是基础路由(开发中) + type: boolean + icon: + description: 菜单图标 + type: string + keepAlive: + description: 是否缓存 + type: boolean + title: + description: 菜单名 + type: string + type: object + system.SysApi: + properties: + ID: + description: 主键ID + type: integer + apiGroup: + description: api组 + type: string + createdAt: + description: 创建时间 + type: string + description: + description: api中文描述 + type: string + method: + description: 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE + type: string + path: + description: api路径 + type: string + updatedAt: + description: 更新时间 + type: string + type: object + system.SysAuthority: + properties: + authorityId: + description: 角色ID + type: integer + authorityName: + description: 角色名 + type: string + children: + items: + $ref: '#/definitions/system.SysAuthority' + type: array + createdAt: + description: 创建时间 + type: string + dataAuthorityId: + items: + $ref: '#/definitions/system.SysAuthority' + type: array + defaultRouter: + description: 默认菜单(默认dashboard) + type: string + deletedAt: + type: string + menus: + items: + $ref: '#/definitions/system.SysBaseMenu' + type: array + parentId: + description: 父角色ID + type: integer + updatedAt: + description: 更新时间 + type: string + type: object + system.SysBaseMenu: + properties: + ID: + description: 主键ID + type: integer + authoritys: + items: + $ref: '#/definitions/system.SysAuthority' + type: array + children: + items: + $ref: '#/definitions/system.SysBaseMenu' + type: array + component: + description: 对应前端文件路径 + type: string + createdAt: + description: 创建时间 + type: string + hidden: + description: 是否在列表隐藏 + type: boolean + menuBtn: + items: + $ref: '#/definitions/system.SysBaseMenuBtn' + type: array + meta: + allOf: + - $ref: '#/definitions/system.Meta' + description: 附加属性 + name: + description: 路由name + type: string + parameters: + items: + $ref: '#/definitions/system.SysBaseMenuParameter' + type: array + parentId: + description: 父菜单ID + type: integer + path: + description: 路由path + type: string + sort: + description: 排序标记 + type: integer + updatedAt: + description: 更新时间 + type: string + type: object + system.SysBaseMenuBtn: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + desc: + type: string + name: + type: string + sysBaseMenuID: + type: integer + updatedAt: + description: 更新时间 + type: string + type: object + system.SysBaseMenuParameter: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + key: + description: 地址栏携带参数的key + type: string + sysBaseMenuID: + type: integer + type: + description: 地址栏携带参数为params还是query + type: string + updatedAt: + description: 更新时间 + type: string + value: + description: 地址栏携带参数的值 + type: string + type: object + system.SysDictionary: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + desc: + description: 描述 + type: string + name: + description: 字典名(中) + type: string + status: + description: 状态 + type: boolean + sysDictionaryDetails: + items: + $ref: '#/definitions/system.SysDictionaryDetail' + type: array + type: + description: 字典名(英) + type: string + updatedAt: + description: 更新时间 + type: string + type: object + system.SysDictionaryDetail: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + extend: + description: 扩展值 + type: string + label: + description: 展示值 + type: string + sort: + description: 排序标记 + type: integer + status: + description: 启用状态 + type: boolean + sysDictionaryID: + description: 关联标记 + type: integer + updatedAt: + description: 更新时间 + type: string + value: + description: 字典值 + type: string + type: object + system.SysExportTemplate: + properties: + ID: + description: 主键ID + type: integer + conditions: + items: + $ref: '#/definitions/system.Condition' + type: array + createdAt: + description: 创建时间 + type: string + dbName: + description: 数据库名称 + type: string + joinTemplate: + items: + $ref: '#/definitions/system.JoinTemplate' + type: array + limit: + type: integer + name: + description: 模板名称 + type: string + order: + type: string + tableName: + description: 表名称 + type: string + templateID: + description: 模板标识 + type: string + templateInfo: + description: 模板信息 + type: string + updatedAt: + description: 更新时间 + type: string + type: object + system.SysMenu: + properties: + ID: + description: 主键ID + type: integer + authoritys: + items: + $ref: '#/definitions/system.SysAuthority' + type: array + btns: + additionalProperties: + type: integer + type: object + children: + items: + $ref: '#/definitions/system.SysMenu' + type: array + component: + description: 对应前端文件路径 + type: string + createdAt: + description: 创建时间 + type: string + hidden: + description: 是否在列表隐藏 + type: boolean + menuBtn: + items: + $ref: '#/definitions/system.SysBaseMenuBtn' + type: array + menuId: + type: integer + meta: + allOf: + - $ref: '#/definitions/system.Meta' + description: 附加属性 + name: + description: 路由name + type: string + parameters: + items: + $ref: '#/definitions/system.SysBaseMenuParameter' + type: array + parentId: + description: 父菜单ID + type: integer + path: + description: 路由path + type: string + sort: + description: 排序标记 + type: integer + updatedAt: + description: 更新时间 + type: string + type: object + system.SysOperationRecord: + properties: + ID: + description: 主键ID + type: integer + agent: + description: 代理 + type: string + body: + description: 请求Body + type: string + createdAt: + description: 创建时间 + type: string + error_message: + description: 错误信息 + type: string + ip: + description: 请求ip + type: string + latency: + description: 延迟 + type: string + method: + description: 请求方法 + type: string + path: + description: 请求路径 + type: string + resp: + description: 响应Body + type: string + status: + description: 请求状态 + type: integer + updatedAt: + description: 更新时间 + type: string + user: + $ref: '#/definitions/system.SysUser' + user_id: + description: 用户id + type: integer + type: object + system.SysUser: + properties: + ID: + description: 主键ID + type: integer + authorities: + items: + $ref: '#/definitions/system.SysAuthority' + type: array + authority: + $ref: '#/definitions/system.SysAuthority' + authorityId: + description: 用户角色ID + type: integer + baseColor: + description: 基础颜色 + type: string + createdAt: + description: 创建时间 + type: string + email: + description: 用户邮箱 + type: string + enable: + description: 用户是否被冻结 1正常 2冻结 + type: integer + headerImg: + description: 用户头像 + type: string + nickName: + description: 用户昵称 + type: string + phone: + description: 用户手机号 + type: string + sideMode: + description: 用户侧边主题 + type: string + updatedAt: + description: 更新时间 + type: string + userName: + description: 用户登录名 + type: string + uuid: + description: 用户UUID + type: string + type: object + system.System: + properties: + config: + $ref: '#/definitions/config.Server' + type: object +info: + contact: {} + description: 使用gin+vue进行极速开发的全栈开发基础平台 + title: Gin-Vue-Admin Swagger API接口文档 + version: v2.7.6 +paths: + /api/createApi: + post: + consumes: + - application/json + parameters: + - description: api路径, api中文描述, api组, 方法 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysApi' + produces: + - application/json + responses: + "200": + description: 创建基础api + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建基础api + tags: + - SysApi + /api/deleteApi: + post: + consumes: + - application/json + parameters: + - description: ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysApi' + produces: + - application/json + responses: + "200": + description: 删除api + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除api + tags: + - SysApi + /api/deleteApisByIds: + delete: + consumes: + - application/json + parameters: + - description: ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: 删除选中Api + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除选中Api + tags: + - SysApi + /api/enterSyncApi: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 确认同步API + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 确认同步API + tags: + - SysApi + /api/freshCasbin: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 刷新成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + summary: 刷新casbin缓存 + tags: + - SysApi + /api/getAllApis: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 获取所有的Api 不分页,返回包括api列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysAPIListResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取所有的Api 不分页 + tags: + - SysApi + /api/getApiById: + post: + consumes: + - application/json + parameters: + - description: 根据id获取api + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 根据id获取api,返回包括api详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysAPIResponse' + type: object + security: + - ApiKeyAuth: [] + summary: 根据id获取api + tags: + - SysApi + /api/getApiGroups: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 获取API分组 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取API分组 + tags: + - SysApi + /api/getApiList: + post: + consumes: + - application/json + parameters: + - description: 分页获取API列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SearchApiParams' + produces: + - application/json + responses: + "200": + description: 分页获取API列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取API列表 + tags: + - SysApi + /api/ignoreApi: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 同步API + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 忽略API + tags: + - IgnoreApi + /api/syncApi: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 同步API + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 同步API + tags: + - SysApi + /api/updateApi: + post: + consumes: + - application/json + parameters: + - description: api路径, api中文描述, api组, 方法 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysApi' + produces: + - application/json + responses: + "200": + description: 修改基础api + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 修改基础api + tags: + - SysApi + /authority/copyAuthority: + post: + consumes: + - application/json + parameters: + - description: 旧角色id, 新权限id, 新权限名, 新父角色id + in: body + name: data + required: true + schema: + $ref: '#/definitions/response.SysAuthorityCopyResponse' + produces: + - application/json + responses: + "200": + description: 拷贝角色,返回包括系统角色详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysAuthorityResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 拷贝角色 + tags: + - Authority + /authority/createAuthority: + post: + consumes: + - application/json + parameters: + - description: 权限id, 权限名, 父角色id + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysAuthority' + produces: + - application/json + responses: + "200": + description: 创建角色,返回包括系统角色详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysAuthorityResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建角色 + tags: + - Authority + /authority/deleteAuthority: + post: + consumes: + - application/json + parameters: + - description: 删除角色 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysAuthority' + produces: + - application/json + responses: + "200": + description: 删除角色 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除角色 + tags: + - Authority + /authority/getAuthorityList: + post: + consumes: + - application/json + parameters: + - description: 页码, 每页大小 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: 分页获取角色列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取角色列表 + tags: + - Authority + /authority/setDataAuthority: + post: + consumes: + - application/json + parameters: + - description: 设置角色资源权限 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysAuthority' + produces: + - application/json + responses: + "200": + description: 设置角色资源权限 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置角色资源权限 + tags: + - Authority + /authority/updateAuthority: + post: + consumes: + - application/json + parameters: + - description: 权限id, 权限名, 父角色id + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysAuthority' + produces: + - application/json + responses: + "200": + description: 更新角色信息,返回包括系统角色详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysAuthorityResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新角色信息 + tags: + - Authority + /authorityBtn/canRemoveAuthorityBtn: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 删除成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置权限按钮 + tags: + - AuthorityBtn + /authorityBtn/getAuthorityBtn: + post: + consumes: + - application/json + parameters: + - description: 菜单id, 角色id, 选中的按钮id + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SysAuthorityBtnReq' + produces: + - application/json + responses: + "200": + description: 返回列表成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysAuthorityBtnRes' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取权限按钮 + tags: + - AuthorityBtn + /authorityBtn/setAuthorityBtn: + post: + consumes: + - application/json + parameters: + - description: 菜单id, 角色id, 选中的按钮id + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SysAuthorityBtnReq' + produces: + - application/json + responses: + "200": + description: 返回列表成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置权限按钮 + tags: + - AuthorityBtn + /autoCode/addFunc: + post: + consumes: + - application/json + parameters: + - description: 增加方法 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.AutoCode' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"创建成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 增加方法 + tags: + - AddFunc + /autoCode/createPackage: + post: + consumes: + - application/json + parameters: + - description: 创建package + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SysAutoCodePackageCreate' + produces: + - application/json + responses: + "200": + description: 创建package成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建package + tags: + - AutoCodePackage + /autoCode/createTemp: + post: + consumes: + - application/json + parameters: + - description: 创建自动代码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.AutoCode' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"创建成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 自动代码模板 + tags: + - AutoCodeTemplate + /autoCode/delPackage: + post: + consumes: + - application/json + parameters: + - description: 创建package + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 删除package成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除package + tags: + - AutoCode + /autoCode/delSysHistory: + post: + consumes: + - application/json + parameters: + - description: 请求参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 删除回滚记录 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除回滚记录 + tags: + - AutoCode + /autoCode/getColumn: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 获取当前表所有字段 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取当前表所有字段 + tags: + - AutoCode + /autoCode/getDatabase: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 获取当前所有数据库 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取当前所有数据库 + tags: + - AutoCode + /autoCode/getMeta: + post: + consumes: + - application/json + parameters: + - description: 请求参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 获取meta信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取meta信息 + tags: + - AutoCode + /autoCode/getPackage: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 创建package成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取package + tags: + - AutoCodePackage + /autoCode/getSysHistory: + post: + consumes: + - application/json + parameters: + - description: 请求参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: 查询回滚记录,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 查询回滚记录 + tags: + - AutoCode + /autoCode/getTables: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 获取当前数据库所有表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取当前数据库所有表 + tags: + - AutoCode + /autoCode/getTemplates: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 创建package成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取package + tags: + - AutoCodePackage + /autoCode/installPlugin: + post: + consumes: + - multipart/form-data + parameters: + - description: this is a test file + in: formData + name: plug + required: true + type: file + produces: + - application/json + responses: + "200": + description: 安装插件成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + type: object + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 安装插件 + tags: + - AutoCodePlugin + /autoCode/preview: + post: + consumes: + - application/json + parameters: + - description: 预览创建代码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.AutoCode' + produces: + - application/json + responses: + "200": + description: 预览创建后的代码 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 预览创建后的代码 + tags: + - AutoCodeTemplate + /autoCode/pubPlug: + get: + consumes: + - application/json + parameters: + - description: 插件名称 + in: query + name: plugName + required: true + type: string + produces: + - application/json + responses: + "200": + description: 打包插件成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 打包插件 + tags: + - AutoCodePlugin + /autoCode/rollback: + post: + consumes: + - application/json + parameters: + - description: 请求参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SysAutoHistoryRollBack' + produces: + - application/json + responses: + "200": + description: 回滚自动生成代码 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 回滚自动生成代码 + tags: + - AutoCode + /base/captcha: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysCaptchaResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 生成验证码 + tags: + - Base + /base/login: + post: + parameters: + - description: 用户名, 密码, 验证码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.Login' + produces: + - application/json + responses: + "200": + description: 返回包括用户信息,token,过期时间 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.LoginResponse' + msg: + type: string + type: object + summary: 用户登录 + tags: + - Base + /casbin/UpdateCasbin: + post: + consumes: + - application/json + parameters: + - description: 权限id, 权限模型列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CasbinInReceive' + produces: + - application/json + responses: + "200": + description: 更新角色api权限 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新角色api权限 + tags: + - Casbin + /casbin/getPolicyPathByAuthorityId: + post: + consumes: + - application/json + parameters: + - description: 权限id, 权限模型列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CasbinInReceive' + produces: + - application/json + responses: + "200": + description: 获取权限列表,返回包括casbin详情列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PolicyPathResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取权限列表 + tags: + - Casbin + /customer/customer: + delete: + consumes: + - application/json + parameters: + - description: 客户ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/example.ExaCustomer' + produces: + - application/json + responses: + "200": + description: 删除客户 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除客户 + tags: + - ExaCustomer + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 客户名 + in: query + name: customerName + type: string + - description: 客户手机号 + in: query + name: customerPhoneData + type: string + - description: 管理角色ID + in: query + name: sysUserAuthorityID + type: integer + - description: 管理ID + in: query + name: sysUserId + type: integer + - description: 更新时间 + in: query + name: updatedAt + type: string + produces: + - application/json + responses: + "200": + description: 获取单一客户信息,返回包括客户详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.ExaCustomerResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取单一客户信息 + tags: + - ExaCustomer + post: + consumes: + - application/json + parameters: + - description: 客户用户名, 客户手机号码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/example.ExaCustomer' + produces: + - application/json + responses: + "200": + description: 创建客户 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建客户 + tags: + - ExaCustomer + put: + consumes: + - application/json + parameters: + - description: 客户ID, 客户信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/example.ExaCustomer' + produces: + - application/json + responses: + "200": + description: 更新客户信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新客户信息 + tags: + - ExaCustomer + /customer/customerList: + get: + consumes: + - application/json + parameters: + - description: 关键字 + in: query + name: keyword + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + produces: + - application/json + responses: + "200": + description: 分页获取权限客户列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取权限客户列表 + tags: + - ExaCustomer + /email/emailTest: + post: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"发送成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 发送测试邮件 + tags: + - System + /email/sendEmail: + post: + parameters: + - description: 发送邮件必须的参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/response.Email' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"发送成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 发送邮件 + tags: + - System + /fileUploadAndDownload/breakpointContinue: + post: + consumes: + - multipart/form-data + parameters: + - description: an example for breakpoint resume, 断点续传示例 + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: 断点续传到服务器 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 断点续传到服务器 + tags: + - ExaFileUploadAndDownload + /fileUploadAndDownload/deleteFile: + post: + parameters: + - description: 传入文件里面id即可 + in: body + name: data + required: true + schema: + $ref: '#/definitions/example.ExaFileUploadAndDownload' + produces: + - application/json + responses: + "200": + description: 删除文件 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除文件 + tags: + - ExaFileUploadAndDownload + /fileUploadAndDownload/findFile: + post: + consumes: + - multipart/form-data + parameters: + - description: 上传文件完成 + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: 创建文件,返回包括文件路径 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.FilePathResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建文件 + tags: + - ExaFileUploadAndDownload + /fileUploadAndDownload/getFileList: + post: + consumes: + - application/json + parameters: + - description: 页码, 每页大小 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: 分页文件列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页文件列表 + tags: + - ExaFileUploadAndDownload + /fileUploadAndDownload/removeChunk: + post: + consumes: + - multipart/form-data + parameters: + - description: 删除缓存切片 + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: 删除切片 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除切片 + tags: + - ExaFileUploadAndDownload + /fileUploadAndDownload/upload: + post: + consumes: + - multipart/form-data + parameters: + - description: 上传文件示例 + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: 上传文件示例,返回包括文件详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.ExaFileResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 上传文件示例 + tags: + - ExaFileUploadAndDownload + /info/createInfo: + post: + consumes: + - application/json + parameters: + - description: 创建公告 + in: body + name: data + required: true + schema: + $ref: '#/definitions/model.Info' + produces: + - application/json + responses: + "200": + description: 创建成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建公告 + tags: + - Info + /info/deleteInfo: + delete: + consumes: + - application/json + parameters: + - description: 删除公告 + in: body + name: data + required: true + schema: + $ref: '#/definitions/model.Info' + produces: + - application/json + responses: + "200": + description: 删除成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除公告 + tags: + - Info + /info/deleteInfoByIds: + delete: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 批量删除成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 批量删除公告 + tags: + - Info + /info/findInfo: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 内容 + in: query + name: content + type: string + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 标题 + in: query + name: title + type: string + - description: 更新时间 + in: query + name: updatedAt + type: string + - description: 作者 + in: query + name: userID + type: integer + produces: + - application/json + responses: + "200": + description: 查询成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.Info' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用id查询公告 + tags: + - Info + /info/getInfoDataSource: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 查询成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: object + msg: + type: string + type: object + summary: 获取Info的数据源 + tags: + - Info + /info/getInfoList: + get: + consumes: + - application/json + parameters: + - in: query + name: endCreatedAt + type: string + - description: 关键字 + in: query + name: keyword + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + - in: query + name: startCreatedAt + type: string + produces: + - application/json + responses: + "200": + description: 获取成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取公告列表 + tags: + - Info + /info/getInfoPublic: + get: + consumes: + - application/json + parameters: + - in: query + name: endCreatedAt + type: string + - description: 关键字 + in: query + name: keyword + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + - in: query + name: startCreatedAt + type: string + produces: + - application/json + responses: + "200": + description: 获取成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: object + msg: + type: string + type: object + summary: 不需要鉴权的公告接口 + tags: + - Info + /info/updateInfo: + put: + consumes: + - application/json + parameters: + - description: 更新公告 + in: body + name: data + required: true + schema: + $ref: '#/definitions/model.Info' + produces: + - application/json + responses: + "200": + description: 更新成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新公告 + tags: + - Info + /init/checkdb: + post: + produces: + - application/json + responses: + "200": + description: 初始化用户数据库 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + summary: 初始化用户数据库 + tags: + - CheckDB + /init/initdb: + post: + parameters: + - description: 初始化数据库参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.InitDB' + produces: + - application/json + responses: + "200": + description: 初始化用户数据库 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: string + type: object + summary: 初始化用户数据库 + tags: + - InitDB + /jwt/jsonInBlacklist: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: jwt加入黑名单 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: jwt加入黑名单 + tags: + - Jwt + /menu/addBaseMenu: + post: + consumes: + - application/json + parameters: + - description: 路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysBaseMenu' + produces: + - application/json + responses: + "200": + description: 新增菜单 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 新增菜单 + tags: + - Menu + /menu/addMenuAuthority: + post: + consumes: + - application/json + parameters: + - description: 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.AddMenuAuthorityInfo' + produces: + - application/json + responses: + "200": + description: 增加menu和角色关联关系 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 增加menu和角色关联关系 + tags: + - AuthorityMenu + /menu/deleteBaseMenu: + post: + consumes: + - application/json + parameters: + - description: 菜单id + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 删除菜单 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除菜单 + tags: + - Menu + /menu/getBaseMenuById: + post: + consumes: + - application/json + parameters: + - description: 菜单id + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 根据id获取菜单,返回包括系统菜单列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysBaseMenuResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 根据id获取菜单 + tags: + - Menu + /menu/getBaseMenuTree: + post: + parameters: + - description: 空 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.Empty' + produces: + - application/json + responses: + "200": + description: 获取用户动态路由,返回包括系统菜单列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysBaseMenusResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取用户动态路由 + tags: + - AuthorityMenu + /menu/getMenu: + post: + parameters: + - description: 空 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.Empty' + produces: + - application/json + responses: + "200": + description: 获取用户动态路由,返回包括系统菜单详情列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysMenusResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取用户动态路由 + tags: + - AuthorityMenu + /menu/getMenuAuthority: + post: + consumes: + - application/json + parameters: + - description: 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetAuthorityId' + produces: + - application/json + responses: + "200": + description: 获取指定角色menu + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取指定角色menu + tags: + - AuthorityMenu + /menu/getMenuList: + post: + consumes: + - application/json + parameters: + - description: 页码, 每页大小 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: 分页获取基础menu列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取基础menu列表 + tags: + - Menu + /menu/updateBaseMenu: + post: + consumes: + - application/json + parameters: + - description: 路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysBaseMenu' + produces: + - application/json + responses: + "200": + description: 更新菜单 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新菜单 + tags: + - Menu + /sysDictionary/createSysDictionary: + post: + consumes: + - application/json + parameters: + - description: SysDictionary模型 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysDictionary' + produces: + - application/json + responses: + "200": + description: 创建SysDictionary + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建SysDictionary + tags: + - SysDictionary + /sysDictionary/deleteSysDictionary: + delete: + consumes: + - application/json + parameters: + - description: SysDictionary模型 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysDictionary' + produces: + - application/json + responses: + "200": + description: 删除SysDictionary + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除SysDictionary + tags: + - SysDictionary + /sysDictionary/findSysDictionary: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 描述 + in: query + name: desc + type: string + - description: 字典名(中) + in: query + name: name + type: string + - description: 状态 + in: query + name: status + type: boolean + - description: 字典名(英) + in: query + name: type + type: string + - description: 更新时间 + in: query + name: updatedAt + type: string + produces: + - application/json + responses: + "200": + description: 用id查询SysDictionary + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用id查询SysDictionary + tags: + - SysDictionary + /sysDictionary/getSysDictionaryList: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取SysDictionary列表 + tags: + - SysDictionary + /sysDictionary/updateSysDictionary: + put: + consumes: + - application/json + parameters: + - description: SysDictionary模型 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysDictionary' + produces: + - application/json + responses: + "200": + description: 更新SysDictionary + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新SysDictionary + tags: + - SysDictionary + /sysDictionaryDetail/createSysDictionaryDetail: + post: + consumes: + - application/json + parameters: + - description: SysDictionaryDetail模型 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysDictionaryDetail' + produces: + - application/json + responses: + "200": + description: 创建SysDictionaryDetail + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建SysDictionaryDetail + tags: + - SysDictionaryDetail + /sysDictionaryDetail/deleteSysDictionaryDetail: + delete: + consumes: + - application/json + parameters: + - description: SysDictionaryDetail模型 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysDictionaryDetail' + produces: + - application/json + responses: + "200": + description: 删除SysDictionaryDetail + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除SysDictionaryDetail + tags: + - SysDictionaryDetail + /sysDictionaryDetail/findSysDictionaryDetail: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 扩展值 + in: query + name: extend + type: string + - description: 展示值 + in: query + name: label + type: string + - description: 排序标记 + in: query + name: sort + type: integer + - description: 启用状态 + in: query + name: status + type: boolean + - description: 关联标记 + in: query + name: sysDictionaryID + type: integer + - description: 更新时间 + in: query + name: updatedAt + type: string + - description: 字典值 + in: query + name: value + type: string + produces: + - application/json + responses: + "200": + description: 用id查询SysDictionaryDetail + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用id查询SysDictionaryDetail + tags: + - SysDictionaryDetail + /sysDictionaryDetail/getSysDictionaryDetailList: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 扩展值 + in: query + name: extend + type: string + - description: 关键字 + in: query + name: keyword + type: string + - description: 展示值 + in: query + name: label + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + - description: 排序标记 + in: query + name: sort + type: integer + - description: 启用状态 + in: query + name: status + type: boolean + - description: 关联标记 + in: query + name: sysDictionaryID + type: integer + - description: 更新时间 + in: query + name: updatedAt + type: string + - description: 字典值 + in: query + name: value + type: string + produces: + - application/json + responses: + "200": + description: 分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取SysDictionaryDetail列表 + tags: + - SysDictionaryDetail + /sysDictionaryDetail/updateSysDictionaryDetail: + put: + consumes: + - application/json + parameters: + - description: 更新SysDictionaryDetail + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysDictionaryDetail' + produces: + - application/json + responses: + "200": + description: 更新SysDictionaryDetail + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新SysDictionaryDetail + tags: + - SysDictionaryDetail + /sysExportTemplate/createSysExportTemplate: + post: + consumes: + - application/json + parameters: + - description: 创建导出模板 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysExportTemplate' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"创建成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 创建导出模板 + tags: + - SysExportTemplate + /sysExportTemplate/deleteSysExportTemplate: + delete: + consumes: + - application/json + parameters: + - description: 删除导出模板 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysExportTemplate' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除导出模板 + tags: + - SysExportTemplate + /sysExportTemplate/deleteSysExportTemplateByIds: + delete: + consumes: + - application/json + parameters: + - description: 批量删除导出模板 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"批量删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 批量删除导出模板 + tags: + - SysExportTemplate + /sysExportTemplate/exportExcel: + get: + consumes: + - application/json + produces: + - application/json + responses: {} + security: + - ApiKeyAuth: [] + summary: 导出表格模板 + tags: + - SysExportTemplate + /sysExportTemplate/findSysExportTemplate: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 数据库名称 + in: query + name: dbName + type: string + - in: query + name: limit + type: integer + - description: 模板名称 + in: query + name: name + type: string + - in: query + name: order + type: string + - description: 表名称 + in: query + name: tableName + type: string + - description: 模板标识 + in: query + name: templateID + type: string + - description: 模板信息 + in: query + name: templateInfo + type: string + - description: 更新时间 + in: query + name: updatedAt + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"查询成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 用id查询导出模板 + tags: + - SysExportTemplate + /sysExportTemplate/getSysExportTemplateList: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 数据库名称 + in: query + name: dbName + type: string + - in: query + name: endCreatedAt + type: string + - description: 关键字 + in: query + name: keyword + type: string + - in: query + name: limit + type: integer + - description: 模板名称 + in: query + name: name + type: string + - in: query + name: order + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + - in: query + name: startCreatedAt + type: string + - description: 表名称 + in: query + name: tableName + type: string + - description: 模板标识 + in: query + name: templateID + type: string + - description: 模板信息 + in: query + name: templateInfo + type: string + - description: 更新时间 + in: query + name: updatedAt + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 分页获取导出模板列表 + tags: + - SysExportTemplate + /sysExportTemplate/importExcel: + post: + consumes: + - application/json + produces: + - application/json + responses: {} + security: + - ApiKeyAuth: [] + summary: 导入表格 + tags: + - SysImportTemplate + /sysExportTemplate/updateSysExportTemplate: + put: + consumes: + - application/json + parameters: + - description: 更新导出模板 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysExportTemplate' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"更新成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 更新导出模板 + tags: + - SysExportTemplate + /sysOperationRecord/createSysOperationRecord: + post: + consumes: + - application/json + parameters: + - description: 创建SysOperationRecord + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysOperationRecord' + produces: + - application/json + responses: + "200": + description: 创建SysOperationRecord + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建SysOperationRecord + tags: + - SysOperationRecord + /sysOperationRecord/deleteSysOperationRecord: + delete: + consumes: + - application/json + parameters: + - description: SysOperationRecord模型 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysOperationRecord' + produces: + - application/json + responses: + "200": + description: 删除SysOperationRecord + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除SysOperationRecord + tags: + - SysOperationRecord + /sysOperationRecord/deleteSysOperationRecordByIds: + delete: + consumes: + - application/json + parameters: + - description: 批量删除SysOperationRecord + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: 批量删除SysOperationRecord + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 批量删除SysOperationRecord + tags: + - SysOperationRecord + /sysOperationRecord/findSysOperationRecord: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 代理 + in: query + name: agent + type: string + - description: 请求Body + in: query + name: body + type: string + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 错误信息 + in: query + name: error_message + type: string + - description: 请求ip + in: query + name: ip + type: string + - description: 延迟 + in: query + name: latency + type: string + - description: 请求方法 + in: query + name: method + type: string + - description: 请求路径 + in: query + name: path + type: string + - description: 响应Body + in: query + name: resp + type: string + - description: 请求状态 + in: query + name: status + type: integer + - description: 更新时间 + in: query + name: updatedAt + type: string + - description: 用户id + in: query + name: user_id + type: integer + produces: + - application/json + responses: + "200": + description: 用id查询SysOperationRecord + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用id查询SysOperationRecord + tags: + - SysOperationRecord + /sysOperationRecord/getSysOperationRecordList: + get: + consumes: + - application/json + parameters: + - description: 主键ID + in: query + name: ID + type: integer + - description: 代理 + in: query + name: agent + type: string + - description: 请求Body + in: query + name: body + type: string + - description: 创建时间 + in: query + name: createdAt + type: string + - description: 错误信息 + in: query + name: error_message + type: string + - description: 请求ip + in: query + name: ip + type: string + - description: 关键字 + in: query + name: keyword + type: string + - description: 延迟 + in: query + name: latency + type: string + - description: 请求方法 + in: query + name: method + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + - description: 请求路径 + in: query + name: path + type: string + - description: 响应Body + in: query + name: resp + type: string + - description: 请求状态 + in: query + name: status + type: integer + - description: 更新时间 + in: query + name: updatedAt + type: string + - description: 用户id + in: query + name: user_id + type: integer + produces: + - application/json + responses: + "200": + description: 分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取SysOperationRecord列表 + tags: + - SysOperationRecord + /system/getServerInfo: + post: + produces: + - application/json + responses: + "200": + description: 获取服务器信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取服务器信息 + tags: + - System + /system/getSystemConfig: + post: + produces: + - application/json + responses: + "200": + description: 获取配置文件内容,返回包括系统配置 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysConfigResponse' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取配置文件内容 + tags: + - System + /system/reloadSystem: + post: + produces: + - application/json + responses: + "200": + description: 重启系统 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 重启系统 + tags: + - System + /system/setSystemConfig: + post: + parameters: + - description: 设置配置文件内容 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.System' + produces: + - application/json + responses: + "200": + description: 设置配置文件内容 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置配置文件内容 + tags: + - System + /user/SetSelfInfo: + put: + consumes: + - application/json + parameters: + - description: ID, 用户名, 昵称, 头像链接 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysUser' + produces: + - application/json + responses: + "200": + description: 设置用户信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置用户信息 + tags: + - SysUser + /user/admin_register: + post: + parameters: + - description: 用户名, 昵称, 密码, 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.Register' + produces: + - application/json + responses: + "200": + description: 用户注册账号,返回包括用户信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.SysUserResponse' + msg: + type: string + type: object + summary: 用户注册账号 + tags: + - SysUser + /user/changePassword: + post: + parameters: + - description: 用户名, 原密码, 新密码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.ChangePasswordReq' + produces: + - application/json + responses: + "200": + description: 用户修改密码 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用户修改密码 + tags: + - SysUser + /user/deleteUser: + delete: + consumes: + - application/json + parameters: + - description: 用户ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetById' + produces: + - application/json + responses: + "200": + description: 删除用户 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除用户 + tags: + - SysUser + /user/getUserInfo: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: 获取用户信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取用户信息 + tags: + - SysUser + /user/getUserList: + post: + consumes: + - application/json + parameters: + - description: 页码, 每页大小 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: 分页获取用户列表,返回包括列表,总数,页码,每页数量 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 分页获取用户列表 + tags: + - SysUser + /user/resetPassword: + post: + parameters: + - description: ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysUser' + produces: + - application/json + responses: + "200": + description: 重置用户密码 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 重置用户密码 + tags: + - SysUser + /user/setUserAuthorities: + post: + consumes: + - application/json + parameters: + - description: 用户UUID, 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SetUserAuthorities' + produces: + - application/json + responses: + "200": + description: 设置用户权限 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置用户权限 + tags: + - SysUser + /user/setUserAuthority: + post: + consumes: + - application/json + parameters: + - description: 用户UUID, 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SetUserAuth' + produces: + - application/json + responses: + "200": + description: 设置用户权限 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更改用户权限 + tags: + - SysUser + /user/setUserInfo: + put: + consumes: + - application/json + parameters: + - description: ID, 用户名, 昵称, 头像链接 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysUser' + produces: + - application/json + responses: + "200": + description: 设置用户信息 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置用户信息 + tags: + - SysUser +securityDefinitions: + ApiKeyAuth: + in: header + name: x-token + type: apiKey +swagger: "2.0" diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/global/global.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/global/global.go new file mode 100644 index 000000000..7291d2a30 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/global/global.go @@ -0,0 +1,66 @@ +package global + +import ( + "fmt" + "sync" + + "github.com/gin-gonic/gin" + "github.com/qiniu/qmgo" + + "github.com/flipped-aurora/gin-vue-admin/server/utils/timer" + "github.com/songzhibin97/gkit/cache/local_cache" + + "golang.org/x/sync/singleflight" + + "go.uber.org/zap" + + "github.com/flipped-aurora/gin-vue-admin/server/config" + + "github.com/redis/go-redis/v9" + "github.com/spf13/viper" + "gorm.io/gorm" +) + +var ( + GVA_DB *gorm.DB + GVA_DBList map[string]*gorm.DB + GVA_REDIS redis.UniversalClient + GVA_REDISList map[string]redis.UniversalClient + GVA_MONGO *qmgo.QmgoClient + GVA_CONFIG config.Server + GVA_VP *viper.Viper + // GVA_LOG *oplogging.Logger + GVA_LOG *zap.Logger + GVA_Timer timer.Timer = timer.NewTimerTask() + GVA_Concurrency_Control = &singleflight.Group{} + GVA_ROUTERS gin.RoutesInfo + GVA_ACTIVE_DBNAME *string + BlackCache local_cache.Cache + lock sync.RWMutex +) + +// GetGlobalDBByDBName 通过名称获取db list中的db +func GetGlobalDBByDBName(dbname string) *gorm.DB { + lock.RLock() + defer lock.RUnlock() + return GVA_DBList[dbname] +} + +// MustGetGlobalDBByDBName 通过名称获取db 如果不存在则panic +func MustGetGlobalDBByDBName(dbname string) *gorm.DB { + lock.RLock() + defer lock.RUnlock() + db, ok := GVA_DBList[dbname] + if !ok || db == nil { + panic("db no init") + } + return db +} + +func GetRedis(name string) redis.UniversalClient { + redis, ok := GVA_REDISList[name] + if !ok || redis == nil { + panic(fmt.Sprintf("redis `%s` no init", name)) + } + return redis +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/global/model.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/global/model.go new file mode 100644 index 000000000..9772eb312 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/global/model.go @@ -0,0 +1,14 @@ +package global + +import ( + "time" + + "gorm.io/gorm" +) + +type GVA_MODEL struct { + ID uint `gorm:"primarykey" json:"ID"` // 主键ID + CreatedAt time.Time // 创建时间 + UpdatedAt time.Time // 更新时间 + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/go.mod b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/go.mod new file mode 100644 index 000000000..51b4c660d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/go.mod @@ -0,0 +1,180 @@ +module github.com/flipped-aurora/gin-vue-admin/server + +go 1.22.0 + +toolchain go1.22.2 + +require ( + github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible + github.com/aws/aws-sdk-go v1.55.5 + github.com/casbin/casbin/v2 v2.100.0 + github.com/casbin/gorm-adapter/v3 v3.28.0 + github.com/fsnotify/fsnotify v1.7.0 + github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 + github.com/gin-gonic/gin v1.10.0 + github.com/glebarez/sqlite v1.11.0 + github.com/go-sql-driver/mysql v1.8.1 + github.com/goccy/go-json v0.10.3 + github.com/gofrs/uuid/v5 v5.3.0 + github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/gookit/color v1.5.4 + github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible + github.com/mholt/archiver/v4 v4.0.0-alpha.8 + github.com/mojocn/base64Captcha v1.3.6 + github.com/otiai10/copy v1.14.0 + github.com/pkg/errors v0.9.1 + github.com/qiniu/go-sdk/v7 v7.23.0 + github.com/qiniu/qmgo v1.1.8 + github.com/redis/go-redis/v9 v9.6.2 + github.com/robfig/cron/v3 v3.0.1 + github.com/shirou/gopsutil/v3 v3.24.5 + github.com/songzhibin97/gkit v1.2.13 + github.com/spf13/viper v1.19.0 + github.com/stretchr/testify v1.9.0 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.3 + github.com/tencentyun/cos-go-sdk-v5 v0.7.55 + github.com/unrolled/secure v1.16.0 + github.com/xuri/excelize/v2 v2.9.0 + go.mongodb.org/mongo-driver v1.17.1 + go.uber.org/automaxprocs v1.6.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.28.0 + golang.org/x/sync v0.8.0 + golang.org/x/text v0.19.0 + gorm.io/datatypes v1.2.3 + gorm.io/driver/mysql v1.5.7 + gorm.io/driver/postgres v1.5.9 + gorm.io/driver/sqlserver v1.5.3 + gorm.io/gen v0.3.26 + gorm.io/gorm v1.25.12 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect + github.com/bodgit/plumbing v1.3.0 // indirect + github.com/bodgit/sevenzip v1.5.2 // indirect + github.com/bodgit/windows v1.0.1 // indirect + github.com/bytedance/sonic v1.12.3 // indirect + github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/casbin/govaluate v1.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clbanning/mxj v1.8.4 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dsnet/compress v0.0.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/elastic/go-sysinfo v1.14.2 // indirect + github.com/elastic/go-windows v1.0.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect + github.com/gammazero/toposort v0.1.1 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.22.1 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/microsoft/go-mssqldb v1.7.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/mozillazg/go-httpheader v0.4.0 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/nwaples/rardecode/v2 v2.0.0-beta.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/therootcompany/xz v1.0.1 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.9.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect + github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.uber.org/multierr v1.11.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/arch v0.11.0 // indirect + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect + golang.org/x/image v0.21.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.26.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/hints v1.1.2 // indirect + gorm.io/plugin/dbresolver v1.5.3 // indirect + howett.net/plist v1.0.1 // indirect + modernc.org/fileutil v1.3.0 // indirect + modernc.org/libc v1.61.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.33.1 // indirect +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/go.sum b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/go.sum new file mode 100644 index 000000000..5cb43179e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/go.sum @@ -0,0 +1,882 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= +github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4= +github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= +github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= +github.com/bodgit/sevenzip v1.5.2 h1:acMIYRaqoHAdeu9LhEGGjL9UzBD4RNf9z7+kWDNignI= +github.com/bodgit/sevenzip v1.5.2/go.mod h1:gTGzXA67Yko6/HLSD0iK4kWaWzPlPmLfDO73jTjSRqc= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= +github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= +github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/casbin/casbin/v2 v2.100.0 h1:aeugSNjjHfCrgA22nHkVvw2xsscboHv5r0a13ljQKGQ= +github.com/casbin/casbin/v2 v2.100.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng= +github.com/casbin/gorm-adapter/v3 v3.28.0 h1:ORF8prF6SfaipdgT1fud+r1Tp5J0uul8QaKJHqCPY/o= +github.com/casbin/gorm-adapter/v3 v3.28.0/go.mod h1:aftWi0cla0CC1bHQVrSFzBcX/98IFK28AvuPppCQgTs= +github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYak= +github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= +github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/elastic/go-sysinfo v1.0.2 h1:Wq1bOgnSz7Obl7DbMjbn0tzx1bE5G8Cfy3MVFa6C1Cc= +github.com/elastic/go-sysinfo v1.0.2/go.mod h1:O/D5m1VpYLwGjCYzEt63g3Z1uO3jXfwyzzjiW90t8cY= +github.com/elastic/go-sysinfo v1.14.2 h1:DeIy+pVfdRsd08Nx2Xjh+dUS+jrEEI7LGc29U/BKVWo= +github.com/elastic/go-sysinfo v1.14.2/go.mod h1:jPSuTgXG+dhhh0GKIyI2Cso+w5lPJ5PvVqKlL8LV/Hk= +github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY= +github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= +github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= +github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flipped-aurora/ws v1.0.2 h1:oEUz7sgrbPENvgli7Q4QpC0NIEbJucgR4yjcDMg/AjY= +github.com/flipped-aurora/ws v1.0.2/go.mod h1:RdyM2Fnvxx7f7A6WSmU1aAhDrQIAVW7LS/0LsAUE5mE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc= +github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg= +github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= +github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= +github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg= +github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= +github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= +github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.6+incompatible h1:/2MdLc7zHJqzV7J2uVGaoGymVobB/OHC8wmEyWRaK68= +github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.6+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s= +github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible h1:XQVXdk+WAJ4fSNB6mMRuYNvFWou7BZs6SZB925hPrnk= +github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= +github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= +github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU= +github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw= +github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= +github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w= +github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= +github.com/nwaples/rardecode/v2 v2.0.0-beta.3 h1:evQTW0IjM2GAL5AaPHiQrT+laWohkt5zHKA3yCsGQGU= +github.com/nwaples/rardecode/v2 v2.0.0-beta.3/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0 h1:c8R11WC8m7KNMkTv/0+Be8vvwo4I3/Ut9AC2FW8fX3U= +github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk= +github.com/qiniu/go-sdk/v7 v7.23.0 h1:4wYB4EGE6MBhvjtE/FZH/mIUt/VH6WjzBucU3VfPwhg= +github.com/qiniu/go-sdk/v7 v7.23.0/go.mod h1:OXsAVU5YrLLtVi4iPFpP80jzb3SRBAczrGkcqQmWhcY= +github.com/qiniu/qmgo v1.1.8 h1:E64M+P59aqQpXKI24ClVtluYkLaJLkkeD2hTVhrdMks= +github.com/qiniu/qmgo v1.1.8/go.mod h1:QvZkzWNEv0buWPx0kdZsSs6URhESVubacxFPlITmvB8= +github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk= +github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/songzhibin97/gkit v1.2.13 h1:paY0XJkdRuy9/8k9nTnbdrzo8pC22jIIFldUkOQv5nU= +github.com/songzhibin97/gkit v1.2.13/go.mod h1:38CreNR27eTGaG1UMGihrXqI4xc3nGfYxLVKKVx6Ngg= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= +github.com/tencentyun/cos-go-sdk-v5 v0.7.55 h1:9DfH3umWUd0I2jdqcUxrU1kLfUPOydULNy4T9qN5PF8= +github.com/tencentyun/cos-go-sdk-v5 v0.7.55/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0= +github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= +github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= +github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo= +github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/unrolled/secure v1.15.0 h1:q7x+pdp8jAHnbzxu6UheP8fRlG/rwYTb8TPuQ3rn9Og= +github.com/unrolled/secure v1.15.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= +github.com/unrolled/secure v1.16.0 h1:XgdAsS/Zl50ZfZPRJK6WpicFttfrsFYFd0+ONDBJubU= +github.com/unrolled/secure v1.16.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY= +github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ= +github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE= +github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE= +github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE= +github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= +github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= +go.mongodb.org/mongo-driver v1.17.0 h1:Hp4q2MCjvY19ViwimTs00wHi7G4yzxh4/2+nTx8r40k= +go.mongodb.org/mongo-driver v1.17.0/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= +golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= +golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= +golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= +golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.2 h1:sdn7ZmG4l7JWtMDUb3L98f2Ym7CO5F8mZLlrQJMfF9g= +gorm.io/datatypes v1.2.2/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +gorm.io/datatypes v1.2.3 h1:95ucr9ip9dZMPhB3Tc9zbcoAi62hxYAgHicu7SLjK4g= +gorm.io/datatypes v1.2.3/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= +gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= +gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY= +gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o= +gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg= +gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU= +gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= +howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= +howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4= +modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M= +modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE= +modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM= +modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= +nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/db_list.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/db_list.go new file mode 100644 index 000000000..90eef9ea1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/db_list.go @@ -0,0 +1,36 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "gorm.io/gorm" +) + +const sys = "system" + +func DBList() { + dbMap := make(map[string]*gorm.DB) + for _, info := range global.GVA_CONFIG.DBList { + if info.Disable { + continue + } + switch info.Type { + case "mysql": + dbMap[info.AliasName] = GormMysqlByConfig(config.Mysql{GeneralDB: info.GeneralDB}) + case "mssql": + dbMap[info.AliasName] = GormMssqlByConfig(config.Mssql{GeneralDB: info.GeneralDB}) + case "pgsql": + dbMap[info.AliasName] = GormPgSqlByConfig(config.Pgsql{GeneralDB: info.GeneralDB}) + case "oracle": + dbMap[info.AliasName] = GormOracleByConfig(config.Oracle{GeneralDB: info.GeneralDB}) + default: + continue + } + } + // 做特殊判断,是否有迁移 + // 适配低版本迁移多数据库版本 + if sysDB, ok := dbMap[sys]; ok { + global.GVA_DB = sysDB + } + global.GVA_DBList = dbMap +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/ensure_tables.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/ensure_tables.go new file mode 100644 index 000000000..daa79f353 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/ensure_tables.go @@ -0,0 +1,106 @@ +package initialize + +import ( + "context" + adapter "github.com/casbin/gorm-adapter/v3" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "gorm.io/gorm" +) + +const initOrderEnsureTables = system.InitOrderExternal - 1 + +type ensureTables struct{} + +// auto run +func init() { + system.RegisterInit(initOrderEnsureTables, &ensureTables{}) +} + +func (ensureTables) InitializerName() string { + return "ensure_tables_created" +} +func (e *ensureTables) InitializeData(ctx context.Context) (next context.Context, err error) { + return ctx, nil +} + +func (e *ensureTables) DataInserted(ctx context.Context) bool { + return true +} + +func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + tables := []interface{}{ + sysModel.SysApi{}, + sysModel.SysUser{}, + sysModel.SysBaseMenu{}, + sysModel.SysAuthority{}, + sysModel.JwtBlacklist{}, + sysModel.SysDictionary{}, + sysModel.SysAutoCodeHistory{}, + sysModel.SysOperationRecord{}, + sysModel.SysDictionaryDetail{}, + sysModel.SysBaseMenuParameter{}, + sysModel.SysBaseMenuBtn{}, + sysModel.SysAuthorityBtn{}, + sysModel.SysAutoCodePackage{}, + sysModel.SysExportTemplate{}, + sysModel.Condition{}, + sysModel.JoinTemplate{}, + sysModel.SysParams{}, + + adapter.CasbinRule{}, + + example.ExaFile{}, + example.ExaCustomer{}, + example.ExaFileChunk{}, + example.ExaFileUploadAndDownload{}, + } + for _, t := range tables { + _ = db.AutoMigrate(&t) + // 视图 authority_menu 会被当成表来创建,引发冲突错误(更新版本的gorm似乎不会) + // 由于 AutoMigrate() 基本无需考虑错误,因此显式忽略 + } + return ctx, nil +} + +func (e *ensureTables) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + tables := []interface{}{ + sysModel.SysApi{}, + sysModel.SysUser{}, + sysModel.SysBaseMenu{}, + sysModel.SysAuthority{}, + sysModel.JwtBlacklist{}, + sysModel.SysDictionary{}, + sysModel.SysAutoCodeHistory{}, + sysModel.SysOperationRecord{}, + sysModel.SysDictionaryDetail{}, + sysModel.SysBaseMenuParameter{}, + sysModel.SysBaseMenuBtn{}, + sysModel.SysAuthorityBtn{}, + sysModel.SysAutoCodePackage{}, + sysModel.SysExportTemplate{}, + sysModel.Condition{}, + sysModel.JoinTemplate{}, + + adapter.CasbinRule{}, + + example.ExaFile{}, + example.ExaCustomer{}, + example.ExaFileChunk{}, + example.ExaFileUploadAndDownload{}, + } + yes := true + for _, t := range tables { + yes = yes && db.Migrator().HasTable(t) + } + return yes +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm.go new file mode 100644 index 000000000..398a823e4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm.go @@ -0,0 +1,77 @@ +package initialize + +import ( + "os" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +func Gorm() *gorm.DB { + switch global.GVA_CONFIG.System.DbType { + case "mysql": + global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Mysql.Dbname + return GormMysql() + case "pgsql": + global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Pgsql.Dbname + return GormPgSql() + case "oracle": + global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Oracle.Dbname + return GormOracle() + case "mssql": + global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Mssql.Dbname + return GormMssql() + case "sqlite": + global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Sqlite.Dbname + return GormSqlite() + default: + global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Mysql.Dbname + return GormMysql() + } +} + +func RegisterTables() { + db := global.GVA_DB + err := db.AutoMigrate( + + system.SysApi{}, + system.SysIgnoreApi{}, + system.SysUser{}, + system.SysBaseMenu{}, + system.JwtBlacklist{}, + system.SysAuthority{}, + system.SysDictionary{}, + system.SysOperationRecord{}, + system.SysAutoCodeHistory{}, + system.SysDictionaryDetail{}, + system.SysBaseMenuParameter{}, + system.SysBaseMenuBtn{}, + system.SysAuthorityBtn{}, + system.SysAutoCodePackage{}, + system.SysExportTemplate{}, + system.Condition{}, + system.JoinTemplate{}, + system.SysParams{}, + + example.ExaFile{}, + example.ExaCustomer{}, + example.ExaFileChunk{}, + example.ExaFileUploadAndDownload{}, + ) + if err != nil { + global.GVA_LOG.Error("register table failed", zap.Error(err)) + os.Exit(0) + } + + err = bizModel() + + if err != nil { + global.GVA_LOG.Error("register biz_table failed", zap.Error(err)) + os.Exit(0) + } + global.GVA_LOG.Info("register table success") +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_biz.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_biz.go new file mode 100644 index 000000000..9316ccc88 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_biz.go @@ -0,0 +1,14 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +func bizModel() error { + db := global.GVA_DB + err := db.AutoMigrate() + if err != nil { + return err + } + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_mssql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_mssql.go new file mode 100644 index 000000000..0ec25a7f5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_mssql.go @@ -0,0 +1,59 @@ +/* + * @Author: 逆光飞翔 191180776@qq.com + * @Date: 2022-12-08 17:25:49 + * @LastEditors: 逆光飞翔 191180776@qq.com + * @LastEditTime: 2022-12-08 18:00:00 + * @FilePath: \server\initialize\gorm_mssql.go + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize/internal" + "gorm.io/driver/sqlserver" + "gorm.io/gorm" +) + +// GormMssql 初始化Mssql数据库 +// Author [LouisZhang](191180776@qq.com) +func GormMssql() *gorm.DB { + m := global.GVA_CONFIG.Mssql + if m.Dbname == "" { + return nil + } + mssqlConfig := sqlserver.Config{ + DSN: m.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + } + if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil { + return nil + } else { + db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine) + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + return db + } +} + +// GormMssqlByConfig 初始化Mysql数据库用过传入配置 +func GormMssqlByConfig(m config.Mssql) *gorm.DB { + if m.Dbname == "" { + return nil + } + mssqlConfig := sqlserver.Config{ + DSN: m.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + } + if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil { + panic(err) + } else { + db.InstanceSet("gorm:table_options", "ENGINE=InnoDB") + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + return db + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_mysql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_mysql.go new file mode 100644 index 000000000..6e496a4d3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_mysql.go @@ -0,0 +1,55 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize/internal" + _ "github.com/go-sql-driver/mysql" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +// GormMysql 初始化Mysql数据库 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func GormMysql() *gorm.DB { + m := global.GVA_CONFIG.Mysql + if m.Dbname == "" { + return nil + } + mysqlConfig := mysql.Config{ + DSN: m.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + SkipInitializeWithVersion: false, // 根据版本自动配置 + } + if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil { + return nil + } else { + db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine) + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + return db + } +} + +// GormMysqlByConfig 初始化Mysql数据库用过传入配置 +func GormMysqlByConfig(m config.Mysql) *gorm.DB { + if m.Dbname == "" { + return nil + } + mysqlConfig := mysql.Config{ + DSN: m.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + SkipInitializeWithVersion: false, // 根据版本自动配置 + } + if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil { + panic(err) + } else { + db.InstanceSet("gorm:table_options", "ENGINE=InnoDB") + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + return db + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_oracle.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_oracle.go new file mode 100644 index 000000000..4d18c8a84 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_oracle.go @@ -0,0 +1,52 @@ +package initialize + +import ( + //"github.com/dzwvip/oracle" + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize/internal" + + //_ "github.com/godror/godror" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +// GormOracle 初始化oracle数据库 +// 如果需要Oracle库 放开import里的注释 把下方 mysql.Config 改为 oracle.Config ; mysql.New 改为 oracle.New +func GormOracle() *gorm.DB { + m := global.GVA_CONFIG.Oracle + if m.Dbname == "" { + return nil + } + oracleConfig := mysql.Config{ + DSN: m.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + } + if db, err := gorm.Open(mysql.New(oracleConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil { + panic(err) + } else { + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + return db + } +} + +// GormOracleByConfig 初始化Oracle数据库用过传入配置 +func GormOracleByConfig(m config.Oracle) *gorm.DB { + if m.Dbname == "" { + return nil + } + oracleConfig := mysql.Config{ + DSN: m.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + } + if db, err := gorm.Open(mysql.New(oracleConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil { + panic(err) + } else { + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + return db + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_pgsql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_pgsql.go new file mode 100644 index 000000000..625c87385 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_pgsql.go @@ -0,0 +1,50 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize/internal" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +// GormPgSql 初始化 Postgresql 数据库 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func GormPgSql() *gorm.DB { + p := global.GVA_CONFIG.Pgsql + if p.Dbname == "" { + return nil + } + pgsqlConfig := postgres.Config{ + DSN: p.Dsn(), // DSN data source name + PreferSimpleProtocol: false, + } + if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil { + return nil + } else { + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(p.MaxIdleConns) + sqlDB.SetMaxOpenConns(p.MaxOpenConns) + return db + } +} + +// GormPgSqlByConfig 初始化 Postgresql 数据库 通过参数 +func GormPgSqlByConfig(p config.Pgsql) *gorm.DB { + if p.Dbname == "" { + return nil + } + pgsqlConfig := postgres.Config{ + DSN: p.Dsn(), // DSN data source name + PreferSimpleProtocol: false, + } + if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil { + panic(err) + } else { + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(p.MaxIdleConns) + sqlDB.SetMaxOpenConns(p.MaxOpenConns) + return db + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_sqlite.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_sqlite.go new file mode 100644 index 000000000..041264107 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/gorm_sqlite.go @@ -0,0 +1,42 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize/internal" + "github.com/glebarez/sqlite" + "gorm.io/gorm" +) + +// GormSqlite 初始化Sqlite数据库 +func GormSqlite() *gorm.DB { + s := global.GVA_CONFIG.Sqlite + if s.Dbname == "" { + return nil + } + + if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil { + panic(err) + } else { + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(s.MaxIdleConns) + sqlDB.SetMaxOpenConns(s.MaxOpenConns) + return db + } +} + +// GormSqliteByConfig 初始化Sqlite数据库用过传入配置 +func GormSqliteByConfig(s config.Sqlite) *gorm.DB { + if s.Dbname == "" { + return nil + } + + if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil { + panic(err) + } else { + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(s.MaxIdleConns) + sqlDB.SetMaxOpenConns(s.MaxOpenConns) + return db + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/gorm.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/gorm.go new file mode 100644 index 000000000..dcf388bea --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/gorm.go @@ -0,0 +1,48 @@ +package internal + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" + "log" + "os" + "time" +) + +var Gorm = new(_gorm) + +type _gorm struct{} + +// Config gorm 自定义配置 +// Author [SliverHorn](https://github.com/SliverHorn) +func (g *_gorm) Config(prefix string, singular bool) *gorm.Config { + var general config.GeneralDB + switch global.GVA_CONFIG.System.DbType { + case "mysql": + general = global.GVA_CONFIG.Mysql.GeneralDB + case "pgsql": + general = global.GVA_CONFIG.Pgsql.GeneralDB + case "oracle": + general = global.GVA_CONFIG.Oracle.GeneralDB + case "sqlite": + general = global.GVA_CONFIG.Sqlite.GeneralDB + case "mssql": + general = global.GVA_CONFIG.Mssql.GeneralDB + default: + general = global.GVA_CONFIG.Mysql.GeneralDB + } + return &gorm.Config{ + Logger: logger.New(NewWriter(general, log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{ + SlowThreshold: 200 * time.Millisecond, + LogLevel: general.LogLevel(), + Colorful: true, + }), + NamingStrategy: schema.NamingStrategy{ + TablePrefix: prefix, + SingularTable: singular, + }, + DisableForeignKeyConstraintWhenMigrating: true, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/gorm_logger_writer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/gorm_logger_writer.go new file mode 100644 index 000000000..955503d87 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/gorm_logger_writer.go @@ -0,0 +1,37 @@ +package internal + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/config" + "go.uber.org/zap" + "gorm.io/gorm/logger" +) + +type Writer struct { + config config.GeneralDB + writer logger.Writer +} + +func NewWriter(config config.GeneralDB, writer logger.Writer) *Writer { + return &Writer{config: config, writer: writer} +} + +// Printf 格式化打印日志 +func (c *Writer) Printf(message string, data ...any) { + if c.config.LogZap { + switch c.config.LogLevel() { + case logger.Silent: + zap.L().Debug(fmt.Sprintf(message, data...)) + case logger.Error: + zap.L().Error(fmt.Sprintf(message, data...)) + case logger.Warn: + zap.L().Warn(fmt.Sprintf(message, data...)) + case logger.Info: + zap.L().Info(fmt.Sprintf(message, data...)) + default: + zap.L().Info(fmt.Sprintf(message, data...)) + } + return + } + c.writer.Printf(message, data...) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/mongo.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/mongo.go new file mode 100644 index 000000000..c4992d712 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/internal/mongo.go @@ -0,0 +1,29 @@ +package internal + +import ( + "context" + "fmt" + "github.com/qiniu/qmgo/options" + "go.mongodb.org/mongo-driver/event" + opt "go.mongodb.org/mongo-driver/mongo/options" + "go.uber.org/zap" +) + +var Mongo = new(mongo) + +type mongo struct{} + +func (m *mongo) GetClientOptions() []options.ClientOptions { + cmdMonitor := &event.CommandMonitor{ + Started: func(ctx context.Context, event *event.CommandStartedEvent) { + zap.L().Info(fmt.Sprintf("[MongoDB][RequestID:%d][database:%s] %s\n", event.RequestID, event.DatabaseName, event.Command), zap.String("business", "mongo")) + }, + Succeeded: func(ctx context.Context, event *event.CommandSucceededEvent) { + zap.L().Info(fmt.Sprintf("[MongoDB][RequestID:%d] [%s] %s\n", event.RequestID, event.Duration.String(), event.Reply), zap.String("business", "mongo")) + }, + Failed: func(ctx context.Context, event *event.CommandFailedEvent) { + zap.L().Error(fmt.Sprintf("[MongoDB][RequestID:%d] [%s] %s\n", event.RequestID, event.Duration.String(), event.Failure), zap.String("business", "mongo")) + }, + } + return []options.ClientOptions{{ClientOptions: &opt.ClientOptions{Monitor: cmdMonitor}}} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/mongo.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/mongo.go new file mode 100644 index 000000000..d88afaa74 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/mongo.go @@ -0,0 +1,151 @@ +package initialize + +import ( + "context" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize/internal" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/pkg/errors" + "github.com/qiniu/qmgo" + "github.com/qiniu/qmgo/options" + "go.mongodb.org/mongo-driver/bson" + option "go.mongodb.org/mongo-driver/mongo/options" + "sort" + "strings" +) + +var Mongo = new(mongo) + +type ( + mongo struct{} + Index struct { + V any `bson:"v"` + Ns any `bson:"ns"` + Key []bson.E `bson:"key"` + Name string `bson:"name"` + } +) + +func (m *mongo) Indexes(ctx context.Context) error { + // 表名:索引列表 列: "表名": [][]string{{"index1", "index2"}} + indexMap := map[string][][]string{} + for collection, indexes := range indexMap { + err := m.CreateIndexes(ctx, collection, indexes) + if err != nil { + return err + } + } + return nil +} + +func (m *mongo) Initialization() error { + var opts []options.ClientOptions + if global.GVA_CONFIG.Mongo.IsZap { + opts = internal.Mongo.GetClientOptions() + } + ctx := context.Background() + client, err := qmgo.Open(ctx, &qmgo.Config{ + Uri: global.GVA_CONFIG.Mongo.Uri(), + Coll: global.GVA_CONFIG.Mongo.Coll, + Database: global.GVA_CONFIG.Mongo.Database, + MinPoolSize: &global.GVA_CONFIG.Mongo.MinPoolSize, + MaxPoolSize: &global.GVA_CONFIG.Mongo.MaxPoolSize, + SocketTimeoutMS: &global.GVA_CONFIG.Mongo.SocketTimeoutMs, + ConnectTimeoutMS: &global.GVA_CONFIG.Mongo.ConnectTimeoutMs, + Auth: &qmgo.Credential{ + Username: global.GVA_CONFIG.Mongo.Username, + Password: global.GVA_CONFIG.Mongo.Password, + AuthSource: global.GVA_CONFIG.Mongo.AuthSource, + }, + }, opts...) + if err != nil { + return errors.Wrap(err, "链接mongodb数据库失败!") + } + global.GVA_MONGO = client + err = m.Indexes(ctx) + if err != nil { + return err + } + return nil +} + +func (m *mongo) CreateIndexes(ctx context.Context, name string, indexes [][]string) error { + collection, err := global.GVA_MONGO.Database.Collection(name).CloneCollection() + if err != nil { + return errors.Wrapf(err, "获取[%s]的表对象失败!", name) + } + list, err := collection.Indexes().List(ctx) + if err != nil { + return errors.Wrapf(err, "获取[%s]的索引对象失败!", name) + } + var entities []Index + err = list.All(ctx, &entities) + if err != nil { + return errors.Wrapf(err, "获取[%s]的索引列表失败!", name) + } + length := len(indexes) + indexMap1 := make(map[string][]string, length) + for i := 0; i < length; i++ { + sort.Strings(indexes[i]) // 对索引key进行排序, 在使用bson.M搜索时, bson会自动按照key的字母顺序进行排序 + length1 := len(indexes[i]) + keys := make([]string, 0, length1) + for j := 0; j < length1; j++ { + if indexes[i][i][0] == '-' { + keys = append(keys, indexes[i][j], "-1") + continue + } + keys = append(keys, indexes[i][j], "1") + } + key := strings.Join(keys, "_") + _, o1 := indexMap1[key] + if o1 { + return errors.Errorf("索引[%s]重复!", key) + } + indexMap1[key] = indexes[i] + } + length = len(entities) + indexMap2 := make(map[string]map[string]string, length) + for i := 0; i < length; i++ { + v1, o1 := indexMap2[entities[i].Name] + if !o1 { + keyLength := len(entities[i].Key) + v1 = make(map[string]string, keyLength) + for j := 0; j < keyLength; j++ { + v2, o2 := v1[entities[i].Key[j].Key] + if !o2 { + v1 = make(map[string]string) + } + v2 = entities[i].Key[j].Key + v1[entities[i].Key[j].Key] = v2 + indexMap2[entities[i].Name] = v1 + } + } + } + for k1, v1 := range indexMap1 { + _, o2 := indexMap2[k1] + if o2 { + continue + } // 索引存在 + if len(fmt.Sprintf("%s.%s.$%s", collection.Name(), name, v1)) > 127 { + err = global.GVA_MONGO.Database.Collection(name).CreateOneIndex(ctx, options.IndexModel{ + Key: v1, + IndexOptions: option.Index().SetName(utils.MD5V([]byte(k1))), + // IndexOptions: option.Index().SetName(utils.MD5V([]byte(k1))).SetExpireAfterSeconds(86400), // SetExpireAfterSeconds(86400) 设置索引过期时间, 86400 = 1天 + }) + if err != nil { + return errors.Wrapf(err, "创建索引[%s]失败!", k1) + } + return nil + } + err = global.GVA_MONGO.Database.Collection(name).CreateOneIndex(ctx, options.IndexModel{ + Key: v1, + IndexOptions: option.Index().SetExpireAfterSeconds(86400), + // IndexOptions: option.Index().SetName(utils.MD5V([]byte(k1))).SetExpireAfterSeconds(86400), // SetExpireAfterSeconds(86400) 设置索引过期时间(秒), 86400 = 1天 + }) + if err != nil { + return errors.Wrapf(err, "创建索引[%s]失败!", k1) + } + } + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/other.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/other.go new file mode 100644 index 000000000..f272a812a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/other.go @@ -0,0 +1,32 @@ +package initialize + +import ( + "bufio" + "github.com/songzhibin97/gkit/cache/local_cache" + "os" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/utils" +) + +func OtherInit() { + dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) + if err != nil { + panic(err) + } + _, err = utils.ParseDuration(global.GVA_CONFIG.JWT.BufferTime) + if err != nil { + panic(err) + } + + global.BlackCache = local_cache.NewCache( + local_cache.SetDefaultExpire(dr), + ) + file, err := os.Open("go.mod") + if err == nil && global.GVA_CONFIG.AutoCode.Module == "" { + scanner := bufio.NewScanner(file) + scanner.Scan() + global.GVA_CONFIG.AutoCode.Module = strings.TrimPrefix(scanner.Text(), "module ") + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin.go new file mode 100644 index 000000000..16913b18f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin.go @@ -0,0 +1,15 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/gin-gonic/gin" +) + +func InstallPlugin(PrivateGroup *gin.RouterGroup, PublicRouter *gin.RouterGroup, engine *gin.Engine) { + if global.GVA_DB == nil { + global.GVA_LOG.Info("项目暂未初始化,无法安装插件,初始化后重启项目即可完成插件安装") + return + } + bizPluginV1(PrivateGroup, PublicRouter) + bizPluginV2(engine) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin_biz_v1.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin_biz_v1.go new file mode 100644 index 000000000..7366c65d9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin_biz_v1.go @@ -0,0 +1,34 @@ +package initialize + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email" + "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin" + "github.com/gin-gonic/gin" +) + +func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) { + for i := range Plugin { + fmt.Println(Plugin[i].RouterPath(), "注册开始!") + PluginGroup := group.Group(Plugin[i].RouterPath()) + Plugin[i].Register(PluginGroup) + fmt.Println(Plugin[i].RouterPath(), "注册成功!") + } +} + +func bizPluginV1(group ...*gin.RouterGroup) { + private := group[0] + public := group[1] + // 添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同 + PluginInit(private, email.CreateEmailPlug( + global.GVA_CONFIG.Email.To, + global.GVA_CONFIG.Email.From, + global.GVA_CONFIG.Email.Host, + global.GVA_CONFIG.Email.Secret, + global.GVA_CONFIG.Email.Nickname, + global.GVA_CONFIG.Email.Port, + global.GVA_CONFIG.Email.IsSSL, + )) + holder(public, private) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin_biz_v2.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin_biz_v2.go new file mode 100644 index 000000000..9d13bbe02 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/plugin_biz_v2.go @@ -0,0 +1,16 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement" + "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin/v2" + "github.com/gin-gonic/gin" +) + +func PluginInitV2(group *gin.Engine, plugins ...plugin.Plugin) { + for i := 0; i < len(plugins); i++ { + plugins[i].Register(group) + } +} +func bizPluginV2(engine *gin.Engine) { + PluginInitV2(engine, announcement.Plugin) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/redis.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/redis.go new file mode 100644 index 000000000..2d9c8f4eb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/redis.go @@ -0,0 +1,59 @@ +package initialize + +import ( + "context" + + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + + "github.com/redis/go-redis/v9" + "go.uber.org/zap" +) + +func initRedisClient(redisCfg config.Redis) (redis.UniversalClient, error) { + var client redis.UniversalClient + // 使用集群模式 + if redisCfg.UseCluster { + client = redis.NewClusterClient(&redis.ClusterOptions{ + Addrs: redisCfg.ClusterAddrs, + Password: redisCfg.Password, + }) + } else { + // 使用单例模式 + client = redis.NewClient(&redis.Options{ + Addr: redisCfg.Addr, + Password: redisCfg.Password, + DB: redisCfg.DB, + }) + } + pong, err := client.Ping(context.Background()).Result() + if err != nil { + global.GVA_LOG.Error("redis connect ping failed, err:", zap.String("name", redisCfg.Name), zap.Error(err)) + return nil, err + } + + global.GVA_LOG.Info("redis connect ping response:", zap.String("name", redisCfg.Name), zap.String("pong", pong)) + return client, nil +} + +func Redis() { + redisClient, err := initRedisClient(global.GVA_CONFIG.Redis) + if err != nil { + panic(err) + } + global.GVA_REDIS = redisClient +} + +func RedisList() { + redisMap := make(map[string]redis.UniversalClient) + + for _, redisCfg := range global.GVA_CONFIG.RedisList { + client, err := initRedisClient(redisCfg) + if err != nil { + panic(err) + } + redisMap[redisCfg.Name] = client + } + + global.GVA_REDISList = redisMap +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/register_init.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/register_init.go new file mode 100644 index 000000000..a2496612b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/register_init.go @@ -0,0 +1,10 @@ +package initialize + +import ( + _ "github.com/flipped-aurora/gin-vue-admin/server/source/example" + _ "github.com/flipped-aurora/gin-vue-admin/server/source/system" +) + +func init() { + // do nothing,only import source package so that inits can be registered +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/router.go new file mode 100644 index 000000000..499f87d8b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/router.go @@ -0,0 +1,110 @@ +package initialize + +import ( + "net/http" + "os" + + "github.com/flipped-aurora/gin-vue-admin/server/docs" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/flipped-aurora/gin-vue-admin/server/router" + "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" +) + +type justFilesFilesystem struct { + fs http.FileSystem +} + +func (fs justFilesFilesystem) Open(name string) (http.File, error) { + f, err := fs.fs.Open(name) + if err != nil { + return nil, err + } + + stat, err := f.Stat() + if stat.IsDir() { + return nil, os.ErrPermission + } + + return f, nil +} + +// 初始化总路由 + +func Routers() *gin.Engine { + Router := gin.New() + Router.Use(gin.Recovery()) + if gin.Mode() == gin.DebugMode { + Router.Use(gin.Logger()) + } + + systemRouter := router.RouterGroupApp.System + exampleRouter := router.RouterGroupApp.Example + // 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的 + // VUE_APP_BASE_API = / + // VUE_APP_BASE_PATH = http://localhost + // 然后执行打包命令 npm run build。在打开下面3行注释 + // Router.Static("/favicon.ico", "./dist/favicon.ico") + // Router.Static("/assets", "./dist/assets") // dist里面的静态资源 + // Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面 + + Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{http.Dir(global.GVA_CONFIG.Local.StorePath)}) // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件") + // 跨域,如需跨域可以打开下面的注释 + // Router.Use(middleware.Cors()) // 直接放行全部跨域请求 + // Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求 + // global.GVA_LOG.Info("use middleware cors") + docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix + Router.GET(global.GVA_CONFIG.System.RouterPrefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + global.GVA_LOG.Info("register swagger handler") + // 方便统一添加路由组前缀 多服务器上线使用 + + PublicGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix) + PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix) + + PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) + + { + // 健康监测 + PublicGroup.GET("/health", func(c *gin.Context) { + c.JSON(http.StatusOK, "ok") + }) + } + { + systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权 + systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关 + } + + { + systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由 + systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由 + systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由 + systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由 + systemRouter.InitSystemRouter(PrivateGroup) // system相关路由 + systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由 + systemRouter.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码 + systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由 + systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理 + systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史 + systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录 + systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理 + systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理 + systemRouter.InitSysExportTemplateRouter(PrivateGroup) // 导出模板 + systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理 + exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由 + exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由 + + } + + //插件路由安装 + InstallPlugin(PrivateGroup, PublicGroup, Router) + + // 注册业务路由 + initBizRouter(PrivateGroup, PublicGroup) + + global.GVA_ROUTERS = Router.Routes() + + global.GVA_LOG.Info("router register success") + return Router +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/router_biz.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/router_biz.go new file mode 100644 index 000000000..9a7bddfef --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/router_biz.go @@ -0,0 +1,16 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/router" + "github.com/gin-gonic/gin" +) + +func holder(routers ...*gin.RouterGroup) { + _ = routers + _ = router.RouterGroupApp +} +func initBizRouter(routers ...*gin.RouterGroup) { + privateGroup := routers[0] + publicGroup := routers[1] + holder(publicGroup, privateGroup) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/timer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/timer.go new file mode 100644 index 000000000..ab5a7a5a4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/timer.go @@ -0,0 +1,37 @@ +package initialize + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/task" + + "github.com/robfig/cron/v3" + + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +func Timer() { + go func() { + var option []cron.Option + option = append(option, cron.WithSeconds()) + // 清理DB定时任务 + _, err := global.GVA_Timer.AddTaskByFunc("ClearDB", "@daily", func() { + err := task.ClearTable(global.GVA_DB) // 定时任务方法定在task文件包中 + if err != nil { + fmt.Println("timer error:", err) + } + }, "定时清理数据库【日志,黑名单】内容", option...) + if err != nil { + fmt.Println("add timer error:", err) + } + + // 其他定时任务定在这里 参考上方使用方法 + + //_, err := global.GVA_Timer.AddTaskByFunc("定时任务标识", "corn表达式", func() { + // 具体执行内容... + // ...... + //}, option...) + //if err != nil { + // fmt.Println("add timer error:", err) + //} + }() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/validator.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/validator.go new file mode 100644 index 000000000..79aea6693 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/initialize/validator.go @@ -0,0 +1,22 @@ +package initialize + +import "github.com/flipped-aurora/gin-vue-admin/server/utils" + +func init() { + _ = utils.RegisterRule("PageVerify", + utils.Rules{ + "Page": {utils.NotEmpty()}, + "PageSize": {utils.NotEmpty()}, + }, + ) + _ = utils.RegisterRule("IdVerify", + utils.Rules{ + "Id": {utils.NotEmpty()}, + }, + ) + _ = utils.RegisterRule("AuthorityIdVerify", + utils.Rules{ + "AuthorityId": {utils.NotEmpty()}, + }, + ) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/main.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/main.go new file mode 100644 index 000000000..f9a7508cb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/core" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize" + _ "go.uber.org/automaxprocs" + "go.uber.org/zap" +) + +//go:generate go env -w GO111MODULE=on +//go:generate go env -w GOPROXY=https://goproxy.cn,direct +//go:generate go mod tidy +//go:generate go mod download + +// @title Gin-Vue-Admin Swagger API接口文档 +// @version v2.7.6 +// @description 使用gin+vue进行极速开发的全栈开发基础平台 +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name x-token +// @BasePath / +func main() { + global.GVA_VP = core.Viper() // 初始化Viper + initialize.OtherInit() + global.GVA_LOG = core.Zap() // 初始化zap日志库 + zap.ReplaceGlobals(global.GVA_LOG) + global.GVA_DB = initialize.Gorm() // gorm连接数据库 + initialize.Timer() + initialize.DBList() + if global.GVA_DB != nil { + initialize.RegisterTables() // 初始化表 + // 程序结束前关闭数据库链接 + db, _ := global.GVA_DB.DB() + defer db.Close() + } + core.RunWindowsServer() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/casbin_rbac.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/casbin_rbac.go new file mode 100644 index 000000000..a1ca4c2b7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/casbin_rbac.go @@ -0,0 +1,36 @@ +package middleware + +import ( + "strconv" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/service" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" +) + +var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService + +// CasbinHandler 拦截器 +func CasbinHandler() gin.HandlerFunc { + return func(c *gin.Context) { + waitUse, _ := utils.GetClaims(c) + //获取请求的PATH + path := c.Request.URL.Path + obj := strings.TrimPrefix(path, global.GVA_CONFIG.System.RouterPrefix) + // 获取请求方法 + act := c.Request.Method + // 获取用户的角色 + sub := strconv.Itoa(int(waitUse.AuthorityId)) + e := casbinService.Casbin() // 判断策略中是否存在 + success, _ := e.Enforce(sub, obj, act) + if !success { + response.FailWithDetailed(gin.H{}, "权限不足", c) + c.Abort() + return + } + c.Next() + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/cors.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/cors.go new file mode 100644 index 000000000..d7e3ccd4c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/cors.go @@ -0,0 +1,73 @@ +package middleware + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/gin-gonic/gin" + "net/http" +) + +// Cors 直接放行所有跨域请求并放行所有 OPTIONS 方法 +func Cors() gin.HandlerFunc { + return func(c *gin.Context) { + method := c.Request.Method + origin := c.Request.Header.Get("Origin") + c.Header("Access-Control-Allow-Origin", origin) + c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id") + c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT") + c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At") + c.Header("Access-Control-Allow-Credentials", "true") + + // 放行所有OPTIONS方法 + if method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + } + // 处理请求 + c.Next() + } +} + +// CorsByRules 按照配置处理跨域请求 +func CorsByRules() gin.HandlerFunc { + // 放行全部 + if global.GVA_CONFIG.Cors.Mode == "allow-all" { + return Cors() + } + return func(c *gin.Context) { + whitelist := checkCors(c.GetHeader("origin")) + + // 通过检查, 添加请求头 + if whitelist != nil { + c.Header("Access-Control-Allow-Origin", whitelist.AllowOrigin) + c.Header("Access-Control-Allow-Headers", whitelist.AllowHeaders) + c.Header("Access-Control-Allow-Methods", whitelist.AllowMethods) + c.Header("Access-Control-Expose-Headers", whitelist.ExposeHeaders) + if whitelist.AllowCredentials { + c.Header("Access-Control-Allow-Credentials", "true") + } + } + + // 严格白名单模式且未通过检查,直接拒绝处理请求 + if whitelist == nil && global.GVA_CONFIG.Cors.Mode == "strict-whitelist" && !(c.Request.Method == "GET" && c.Request.URL.Path == "/health") { + c.AbortWithStatus(http.StatusForbidden) + } else { + // 非严格白名单模式,无论是否通过检查均放行所有 OPTIONS 方法 + if c.Request.Method == http.MethodOptions { + c.AbortWithStatus(http.StatusNoContent) + } + } + + // 处理请求 + c.Next() + } +} + +func checkCors(currentOrigin string) *config.CORSWhitelist { + for _, whitelist := range global.GVA_CONFIG.Cors.Whitelist { + // 遍历配置中的跨域头,寻找匹配项 + if currentOrigin == whitelist.AllowOrigin { + return &whitelist + } + } + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/email.go new file mode 100644 index 000000000..4a07561c9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/email.go @@ -0,0 +1,60 @@ +package middleware + +import ( + "bytes" + "io" + "strconv" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/utils" + utils2 "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +var userService = service.ServiceGroupApp.SystemServiceGroup.UserService + +func ErrorToEmail() gin.HandlerFunc { + return func(c *gin.Context) { + var username string + claims, _ := utils2.GetClaims(c) + if claims.Username != "" { + username = claims.Username + } else { + id, _ := strconv.Atoi(c.Request.Header.Get("x-user-id")) + user, err := userService.FindUserById(id) + if err != nil { + username = "Unknown" + } + username = user.Username + } + body, _ := io.ReadAll(c.Request.Body) + // 再重新写回请求体body中,ioutil.ReadAll会清空c.Request.Body中的数据 + c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) + record := system.SysOperationRecord{ + Ip: c.ClientIP(), + Method: c.Request.Method, + Path: c.Request.URL.Path, + Agent: c.Request.UserAgent(), + Body: string(body), + } + now := time.Now() + + c.Next() + + latency := time.Since(now) + status := c.Writer.Status() + record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String() + str := "接收到的请求为" + record.Body + "\n" + "请求方式为" + record.Method + "\n" + "报错信息如下" + record.ErrorMessage + "\n" + "耗时" + latency.String() + "\n" + if status != 200 { + subject := username + "" + record.Ip + "调用了" + record.Path + "报错了" + if err := utils.ErrorToEmail(subject, str); err != nil { + global.GVA_LOG.Error("ErrorToEmail Failed, err:", zap.Error(err)) + } + } + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/error.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/error.go new file mode 100644 index 000000000..f68b7a562 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/error.go @@ -0,0 +1,61 @@ +package middleware + +import ( + "net" + "net/http" + "net/http/httputil" + "os" + "runtime/debug" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志 +func GinRecovery(stack bool) gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + // Check for a broken connection, as it is not really a + // condition that warrants a panic stack trace. + var brokenPipe bool + if ne, ok := err.(*net.OpError); ok { + if se, ok := ne.Err.(*os.SyscallError); ok { + if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { + brokenPipe = true + } + } + } + + httpRequest, _ := httputil.DumpRequest(c.Request, false) + if brokenPipe { + global.GVA_LOG.Error(c.Request.URL.Path, + zap.Any("error", err), + zap.String("request", string(httpRequest)), + ) + // If the connection is dead, we can't write a status to it. + _ = c.Error(err.(error)) // nolint: errcheck + c.Abort() + return + } + + if stack { + global.GVA_LOG.Error("[Recovery from panic]", + zap.Any("error", err), + zap.String("request", string(httpRequest)), + zap.String("stack", string(debug.Stack())), + ) + } else { + global.GVA_LOG.Error("[Recovery from panic]", + zap.Any("error", err), + zap.String("request", string(httpRequest)), + ) + } + c.AbortWithStatus(http.StatusInternalServerError) + } + }() + c.Next() + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/jwt.go new file mode 100644 index 000000000..38b56dcf3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/jwt.go @@ -0,0 +1,81 @@ +package middleware + +import ( + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/golang-jwt/jwt/v4" + "strconv" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/service" + + "github.com/gin-gonic/gin" +) + +var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService + +func JWTAuth() gin.HandlerFunc { + return func(c *gin.Context) { + // 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录 + token := utils.GetToken(c) + if token == "" { + response.NoAuth("未登录或非法访问", c) + c.Abort() + return + } + if jwtService.IsBlacklist(token) { + response.NoAuth("您的帐户异地登陆或令牌失效", c) + utils.ClearToken(c) + c.Abort() + return + } + j := utils.NewJWT() + // parseToken 解析token包含的信息 + claims, err := j.ParseToken(token) + if err != nil { + if errors.Is(err, utils.TokenExpired) { + response.NoAuth("授权已过期", c) + utils.ClearToken(c) + c.Abort() + return + } + response.NoAuth(err.Error(), c) + utils.ClearToken(c) + c.Abort() + return + } + + // 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开 + // 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开 + + //if user, err := userService.FindUserByUuid(claims.UUID.String()); err != nil || user.Enable == 2 { + // _ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: token}) + // response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c) + // c.Abort() + //} + c.Set("claims", claims) + if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime { + dr, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) + claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr)) + newToken, _ := j.CreateTokenByOldToken(token, *claims) + newClaims, _ := j.ParseToken(newToken) + c.Header("new-token", newToken) + c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10)) + utils.SetToken(c, newToken, int(dr.Seconds())) + if global.GVA_CONFIG.System.UseMultipoint { + // 记录新的活跃jwt + _ = jwtService.SetRedisJWT(newToken, newClaims.Username) + } + } + c.Next() + + if newToken, exists := c.Get("new-token"); exists { + c.Header("new-token", newToken.(string)) + } + if newExpiresAt, exists := c.Get("new-expires-at"); exists { + c.Header("new-expires-at", newExpiresAt.(string)) + } + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/limit_ip.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/limit_ip.go new file mode 100644 index 000000000..315010b22 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/limit_ip.go @@ -0,0 +1,92 @@ +package middleware + +import ( + "context" + "errors" + "net/http" + "time" + + "go.uber.org/zap" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/gin-gonic/gin" +) + +type LimitConfig struct { + // GenerationKey 根据业务生成key 下面CheckOrMark查询生成 + GenerationKey func(c *gin.Context) string + // 检查函数,用户可修改具体逻辑,更加灵活 + CheckOrMark func(key string, expire int, limit int) error + // Expire key 过期时间 + Expire int + // Limit 周期时间 + Limit int +} + +func (l LimitConfig) LimitWithTime() gin.HandlerFunc { + return func(c *gin.Context) { + if err := l.CheckOrMark(l.GenerationKey(c), l.Expire, l.Limit); err != nil { + c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": err}) + c.Abort() + return + } else { + c.Next() + } + } +} + +// DefaultGenerationKey 默认生成key +func DefaultGenerationKey(c *gin.Context) string { + return "GVA_Limit" + c.ClientIP() +} + +func DefaultCheckOrMark(key string, expire int, limit int) (err error) { + // 判断是否开启redis + if global.GVA_REDIS == nil { + return err + } + if err = SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil { + global.GVA_LOG.Error("limit", zap.Error(err)) + } + return err +} + +func DefaultLimit() gin.HandlerFunc { + return LimitConfig{ + GenerationKey: DefaultGenerationKey, + CheckOrMark: DefaultCheckOrMark, + Expire: global.GVA_CONFIG.System.LimitTimeIP, + Limit: global.GVA_CONFIG.System.LimitCountIP, + }.LimitWithTime() +} + +// SetLimitWithTime 设置访问次数 +func SetLimitWithTime(key string, limit int, expiration time.Duration) error { + count, err := global.GVA_REDIS.Exists(context.Background(), key).Result() + if err != nil { + return err + } + if count == 0 { + pipe := global.GVA_REDIS.TxPipeline() + pipe.Incr(context.Background(), key) + pipe.Expire(context.Background(), key, expiration) + _, err = pipe.Exec(context.Background()) + return err + } else { + // 次数 + if times, err := global.GVA_REDIS.Get(context.Background(), key).Int(); err != nil { + return err + } else { + if times >= limit { + if t, err := global.GVA_REDIS.PTTL(context.Background(), key).Result(); err != nil { + return errors.New("请求太过频繁,请稍后再试") + } else { + return errors.New("请求太过频繁, 请 " + t.String() + " 秒后尝试") + } + } else { + return global.GVA_REDIS.Incr(context.Background(), key).Err() + } + } + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/loadtls.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/loadtls.go new file mode 100644 index 000000000..a17cf653b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/loadtls.go @@ -0,0 +1,27 @@ +package middleware + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/unrolled/secure" +) + +// 用https把这个中间件在router里面use一下就好 + +func LoadTls() gin.HandlerFunc { + return func(c *gin.Context) { + middleware := secure.New(secure.Options{ + SSLRedirect: true, + SSLHost: "localhost:443", + }) + err := middleware.Process(c.Writer, c.Request) + if err != nil { + // 如果出现错误,请不要继续 + fmt.Println(err) + return + } + // 继续往下处理 + c.Next() + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/logger.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/logger.go new file mode 100644 index 000000000..fabc33497 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/logger.go @@ -0,0 +1,89 @@ +package middleware + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +// LogLayout 日志layout +type LogLayout struct { + Time time.Time + Metadata map[string]interface{} // 存储自定义原数据 + Path string // 访问路径 + Query string // 携带query + Body string // 携带body数据 + IP string // ip地址 + UserAgent string // 代理 + Error string // 错误 + Cost time.Duration // 花费时间 + Source string // 来源 +} + +type Logger struct { + // Filter 用户自定义过滤 + Filter func(c *gin.Context) bool + // FilterKeyword 关键字过滤(key) + FilterKeyword func(layout *LogLayout) bool + // AuthProcess 鉴权处理 + AuthProcess func(c *gin.Context, layout *LogLayout) + // 日志处理 + Print func(LogLayout) + // Source 服务唯一标识 + Source string +} + +func (l Logger) SetLoggerMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + query := c.Request.URL.RawQuery + var body []byte + if l.Filter != nil && !l.Filter(c) { + body, _ = c.GetRawData() + // 将原body塞回去 + c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) + } + c.Next() + cost := time.Since(start) + layout := LogLayout{ + Time: time.Now(), + Path: path, + Query: query, + IP: c.ClientIP(), + UserAgent: c.Request.UserAgent(), + Error: strings.TrimRight(c.Errors.ByType(gin.ErrorTypePrivate).String(), "\n"), + Cost: cost, + Source: l.Source, + } + if l.Filter != nil && !l.Filter(c) { + layout.Body = string(body) + } + if l.AuthProcess != nil { + // 处理鉴权需要的信息 + l.AuthProcess(c, &layout) + } + if l.FilterKeyword != nil { + // 自行判断key/value 脱敏等 + l.FilterKeyword(&layout) + } + // 自行处理日志 + l.Print(layout) + } +} + +func DefaultLogger() gin.HandlerFunc { + return Logger{ + Print: func(layout LogLayout) { + // 标准输出,k8s做收集 + v, _ := json.Marshal(layout) + fmt.Println(string(v)) + }, + Source: "GVA", + }.SetLoggerMiddleware() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/operation.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/operation.go new file mode 100644 index 000000000..f34cf68ee --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/middleware/operation.go @@ -0,0 +1,133 @@ +package middleware + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService + +var respPool sync.Pool +var bufferSize = 1024 + +func init() { + respPool.New = func() interface{} { + return make([]byte, bufferSize) + } +} + +func OperationRecord() gin.HandlerFunc { + return func(c *gin.Context) { + var body []byte + var userId int + if c.Request.Method != http.MethodGet { + var err error + body, err = io.ReadAll(c.Request.Body) + if err != nil { + global.GVA_LOG.Error("read body from request error:", zap.Error(err)) + } else { + c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) + } + } else { + query := c.Request.URL.RawQuery + query, _ = url.QueryUnescape(query) + split := strings.Split(query, "&") + m := make(map[string]string) + for _, v := range split { + kv := strings.Split(v, "=") + if len(kv) == 2 { + m[kv[0]] = kv[1] + } + } + body, _ = json.Marshal(&m) + } + claims, _ := utils.GetClaims(c) + if claims != nil && claims.BaseClaims.ID != 0 { + userId = int(claims.BaseClaims.ID) + } else { + id, err := strconv.Atoi(c.Request.Header.Get("x-user-id")) + if err != nil { + userId = 0 + } + userId = id + } + record := system.SysOperationRecord{ + Ip: c.ClientIP(), + Method: c.Request.Method, + Path: c.Request.URL.Path, + Agent: c.Request.UserAgent(), + Body: "", + UserID: userId, + } + + // 上传文件时候 中间件日志进行裁断操作 + if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") { + record.Body = "[文件]" + } else { + if len(body) > bufferSize { + record.Body = "[超出记录长度]" + } else { + record.Body = string(body) + } + } + + writer := responseBodyWriter{ + ResponseWriter: c.Writer, + body: &bytes.Buffer{}, + } + c.Writer = writer + now := time.Now() + + c.Next() + + latency := time.Since(now) + record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String() + record.Status = c.Writer.Status() + record.Latency = latency + record.Resp = writer.body.String() + + if strings.Contains(c.Writer.Header().Get("Pragma"), "public") || + strings.Contains(c.Writer.Header().Get("Expires"), "0") || + strings.Contains(c.Writer.Header().Get("Cache-Control"), "must-revalidate, post-check=0, pre-check=0") || + strings.Contains(c.Writer.Header().Get("Content-Type"), "application/force-download") || + strings.Contains(c.Writer.Header().Get("Content-Type"), "application/octet-stream") || + strings.Contains(c.Writer.Header().Get("Content-Type"), "application/vnd.ms-excel") || + strings.Contains(c.Writer.Header().Get("Content-Type"), "application/download") || + strings.Contains(c.Writer.Header().Get("Content-Disposition"), "attachment") || + strings.Contains(c.Writer.Header().Get("Content-Transfer-Encoding"), "binary") { + if len(record.Resp) > bufferSize { + // 截断 + record.Body = "超出记录长度" + } + } + + if err := operationRecordService.CreateSysOperationRecord(record); err != nil { + global.GVA_LOG.Error("create operation record error:", zap.Error(err)) + } + } +} + +type responseBodyWriter struct { + gin.ResponseWriter + body *bytes.Buffer +} + +func (r responseBodyWriter) Write(b []byte) (int, error) { + r.body.Write(b) + return r.ResponseWriter.Write(b) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/basetypes.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/basetypes.go new file mode 100644 index 000000000..870d975c6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/basetypes.go @@ -0,0 +1,36 @@ +package common + +import ( + "database/sql/driver" + "encoding/json" + "errors" +) + +type JSONMap map[string]interface{} + +func (m JSONMap) Value() (driver.Value, error) { + if m == nil { + return nil, nil + } + return json.Marshal(m) +} + +func (m *JSONMap) Scan(value interface{}) error { + if value == nil { + *m = make(map[string]interface{}) + return nil + } + var err error + switch value.(type) { + case []byte: + err = json.Unmarshal(value.([]byte), m) + case string: + err = json.Unmarshal([]byte(value.(string)), m) + default: + err = errors.New("basetypes.JSONMap.Scan: invalid value type") + } + if err != nil { + return err + } + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/clearDB.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/clearDB.go new file mode 100644 index 000000000..e7fc75795 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/clearDB.go @@ -0,0 +1,7 @@ +package common + +type ClearDB struct { + TableName string + CompareField string + Interval string +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/request/common.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/request/common.go new file mode 100644 index 000000000..c729f3db2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/request/common.go @@ -0,0 +1,48 @@ +package request + +import ( + "gorm.io/gorm" +) + +// PageInfo Paging common input parameter structure +type PageInfo struct { + Page int `json:"page" form:"page"` // 页码 + PageSize int `json:"pageSize" form:"pageSize"` // 每页大小 + Keyword string `json:"keyword" form:"keyword"` // 关键字 +} + +func (r *PageInfo) Paginate() func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + if r.Page <= 0 { + r.Page = 1 + } + switch { + case r.PageSize > 100: + r.PageSize = 100 + case r.PageSize <= 0: + r.PageSize = 10 + } + offset := (r.Page - 1) * r.PageSize + return db.Offset(offset).Limit(r.PageSize) + } +} + +// GetById Find by id structure +type GetById struct { + ID int `json:"id" form:"id"` // 主键ID +} + +func (r *GetById) Uint() uint { + return uint(r.ID) +} + +type IdsReq struct { + Ids []int `json:"ids" form:"ids"` +} + +// GetAuthorityId Get role by id structure +type GetAuthorityId struct { + AuthorityId uint `json:"authorityId" form:"authorityId"` // 角色ID +} + +type Empty struct{} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/response/common.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/response/common.go new file mode 100644 index 000000000..74610965b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/response/common.go @@ -0,0 +1,8 @@ +package response + +type PageResult struct { + List interface{} `json:"list"` + Total int64 `json:"total"` + Page int `json:"page"` + PageSize int `json:"pageSize"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/response/response.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/response/response.go new file mode 100644 index 000000000..a429b12e1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/common/response/response.go @@ -0,0 +1,63 @@ +package response + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type Response struct { + Code int `json:"code"` + Data interface{} `json:"data"` + Msg string `json:"msg"` +} + +const ( + ERROR = 7 + SUCCESS = 0 +) + +func Result(code int, data interface{}, msg string, c *gin.Context) { + // 开始时间 + c.JSON(http.StatusOK, Response{ + code, + data, + msg, + }) +} + +func Ok(c *gin.Context) { + Result(SUCCESS, map[string]interface{}{}, "操作成功", c) +} + +func OkWithMessage(message string, c *gin.Context) { + Result(SUCCESS, map[string]interface{}{}, message, c) +} + +func OkWithData(data interface{}, c *gin.Context) { + Result(SUCCESS, data, "成功", c) +} + +func OkWithDetailed(data interface{}, message string, c *gin.Context) { + Result(SUCCESS, data, message, c) +} + +func Fail(c *gin.Context) { + Result(ERROR, map[string]interface{}{}, "操作失败", c) +} + +func FailWithMessage(message string, c *gin.Context) { + Result(ERROR, map[string]interface{}{}, message, c) +} + +func NoAuth(message string, c *gin.Context) { + c.JSON(http.StatusUnauthorized, Response{ + 7, + nil, + message, + }) +} + +func FailWithDetailed(data interface{}, message string, c *gin.Context) { + Result(ERROR, data, message, c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_breakpoint_continue.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_breakpoint_continue.go new file mode 100644 index 000000000..3c2924bdb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_breakpoint_continue.go @@ -0,0 +1,24 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// file struct, 文件结构体 +type ExaFile struct { + global.GVA_MODEL + FileName string + FileMd5 string + FilePath string + ExaFileChunk []ExaFileChunk + ChunkTotal int + IsFinish bool +} + +// file chunk struct, 切片结构体 +type ExaFileChunk struct { + global.GVA_MODEL + ExaFileID uint + FileChunkNumber int + FileChunkPath string +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_customer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_customer.go new file mode 100644 index 000000000..e78dd093f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_customer.go @@ -0,0 +1,15 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type ExaCustomer struct { + global.GVA_MODEL + CustomerName string `json:"customerName" form:"customerName" gorm:"comment:客户名"` // 客户名 + CustomerPhoneData string `json:"customerPhoneData" form:"customerPhoneData" gorm:"comment:客户手机号"` // 客户手机号 + SysUserID uint `json:"sysUserId" form:"sysUserId" gorm:"comment:管理ID"` // 管理ID + SysUserAuthorityID uint `json:"sysUserAuthorityID" form:"sysUserAuthorityID" gorm:"comment:管理角色ID"` // 管理角色ID + SysUser system.SysUser `json:"sysUser" form:"sysUser" gorm:"comment:管理详情"` // 管理详情 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_file_upload_download.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_file_upload_download.go new file mode 100644 index 000000000..bf4c7df7e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/exa_file_upload_download.go @@ -0,0 +1,17 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +type ExaFileUploadAndDownload struct { + global.GVA_MODEL + Name string `json:"name" gorm:"comment:文件名"` // 文件名 + Url string `json:"url" gorm:"comment:文件地址"` // 文件地址 + Tag string `json:"tag" gorm:"comment:文件标签"` // 文件标签 + Key string `json:"key" gorm:"comment:编号"` // 编号 +} + +func (ExaFileUploadAndDownload) TableName() string { + return "exa_file_upload_and_downloads" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_breakpoint_continue.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_breakpoint_continue.go new file mode 100644 index 000000000..54aa3516b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_breakpoint_continue.go @@ -0,0 +1,11 @@ +package response + +import "github.com/flipped-aurora/gin-vue-admin/server/model/example" + +type FilePathResponse struct { + FilePath string `json:"filePath"` +} + +type FileResponse struct { + File example.ExaFile `json:"file"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_customer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_customer.go new file mode 100644 index 000000000..7fd26f9d6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_customer.go @@ -0,0 +1,7 @@ +package response + +import "github.com/flipped-aurora/gin-vue-admin/server/model/example" + +type ExaCustomerResponse struct { + Customer example.ExaCustomer `json:"customer"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_file_upload_download.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_file_upload_download.go new file mode 100644 index 000000000..c1b7931a0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/example/response/exa_file_upload_download.go @@ -0,0 +1,7 @@ +package response + +import "github.com/flipped-aurora/gin-vue-admin/server/model/example" + +type ExaFileResponse struct { + File example.ExaFileUploadAndDownload `json:"file"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/jwt.go new file mode 100644 index 000000000..638820244 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/jwt.go @@ -0,0 +1,21 @@ +package request + +import ( + "github.com/gofrs/uuid/v5" + jwt "github.com/golang-jwt/jwt/v4" +) + +// Custom claims structure +type CustomClaims struct { + BaseClaims + BufferTime int64 + jwt.RegisteredClaims +} + +type BaseClaims struct { + UUID uuid.UUID + ID uint + Username string + NickName string + AuthorityId uint +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_api.go new file mode 100644 index 000000000..0be8ee72d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_api.go @@ -0,0 +1,14 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +// api分页条件查询及排序结构体 +type SearchApiParams struct { + system.SysApi + request.PageInfo + OrderKey string `json:"orderKey"` // 排序 + Desc bool `json:"desc"` // 排序方式:升序false(默认)|降序true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_authority_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_authority_btn.go new file mode 100644 index 000000000..98493ff34 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_authority_btn.go @@ -0,0 +1,7 @@ +package request + +type SysAuthorityBtnReq struct { + MenuID uint `json:"menuID"` + AuthorityId uint `json:"authorityId"` + Selected []uint `json:"selected"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_code.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_code.go new file mode 100644 index 000000000..942d75640 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_code.go @@ -0,0 +1,267 @@ +package request + +import ( + "encoding/json" + "fmt" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/pkg/errors" + "go/token" + "strings" +) + +type AutoCode struct { + Package string `json:"package"` + PackageT string `json:"-"` + TableName string `json:"tableName" example:"表名"` // 表名 + BusinessDB string `json:"businessDB" example:"业务数据库"` // 业务数据库 + StructName string `json:"structName" example:"Struct名称"` // Struct名称 + PackageName string `json:"packageName" example:"文件名称"` // 文件名称 + Description string `json:"description" example:"Struct中文名称"` // Struct中文名称 + Abbreviation string `json:"abbreviation" example:"Struct简称"` // Struct简称 + HumpPackageName string `json:"humpPackageName" example:"go文件名称"` // go文件名称 + GvaModel bool `json:"gvaModel" example:"false"` // 是否使用gva默认Model + AutoMigrate bool `json:"autoMigrate" example:"false"` // 是否自动迁移表结构 + AutoCreateResource bool `json:"autoCreateResource" example:"false"` // 是否自动创建资源标识 + AutoCreateApiToSql bool `json:"autoCreateApiToSql" example:"false"` // 是否自动创建api + AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" example:"false"` // 是否自动创建menu + AutoCreateBtnAuth bool `json:"autoCreateBtnAuth" example:"false"` // 是否自动创建按钮权限 + OnlyTemplate bool `json:"onlyTemplate" example:"false"` // 是否只生成模板 + Fields []*AutoCodeField `json:"fields"` + DictTypes []string `json:"-"` + PrimaryField *AutoCodeField `json:"primaryField"` + DataSourceMap map[string]*DataSource `json:"-"` + HasPic bool `json:"-"` + HasFile bool `json:"-"` + HasTimer bool `json:"-"` + NeedSort bool `json:"-"` + NeedJSON bool `json:"-"` + HasRichText bool `json:"-"` + HasDataSource bool `json:"-"` + HasSearchTimer bool `json:"-"` + HasArray bool `json:"-"` + HasExcel bool `json:"-"` +} + +type DataSource struct { + DBName string `json:"dbName"` + Table string `json:"table"` + Label string `json:"label"` + Value string `json:"value"` + Association int `json:"association"` // 关联关系 1 一对一 2 一对多 +} + +func (r *AutoCode) Apis() []model.SysApi { + return []model.SysApi{ + { + Path: "/" + r.Abbreviation + "/" + "create" + r.StructName, + Description: "新增" + r.Description, + ApiGroup: r.Description, + Method: "POST", + }, + { + Path: "/" + r.Abbreviation + "/" + "delete" + r.StructName, + Description: "删除" + r.Description, + ApiGroup: r.Description, + Method: "DELETE", + }, + { + Path: "/" + r.Abbreviation + "/" + "delete" + r.StructName + "ByIds", + Description: "批量删除" + r.Description, + ApiGroup: r.Description, + Method: "DELETE", + }, + { + Path: "/" + r.Abbreviation + "/" + "update" + r.StructName, + Description: "更新" + r.Description, + ApiGroup: r.Description, + Method: "PUT", + }, + { + Path: "/" + r.Abbreviation + "/" + "find" + r.StructName, + Description: "根据ID获取" + r.Description, + ApiGroup: r.Description, + Method: "GET", + }, + { + Path: "/" + r.Abbreviation + "/" + "get" + r.StructName + "List", + Description: "获取" + r.Description + "列表", + ApiGroup: r.Description, + Method: "GET", + }, + } +} + +func (r *AutoCode) Menu(template string) model.SysBaseMenu { + component := fmt.Sprintf("view/%s/%s/%s.vue", r.Package, r.PackageName, r.PackageName) + if template != "package" { + component = fmt.Sprintf("plugin/%s/view/%s.vue", r.Package, r.PackageName) + } + return model.SysBaseMenu{ + ParentId: 0, + Path: r.Abbreviation, + Name: r.Abbreviation, + Component: component, + Meta: model.Meta{ + Title: r.Description, + }, + } +} + +// Pretreatment 预处理 +// Author [SliverHorn](https://github.com/SliverHorn) +func (r *AutoCode) Pretreatment() error { + if token.IsKeyword(r.Abbreviation) { + r.Abbreviation = r.Abbreviation + "_" + } // go 关键字处理 + if strings.HasSuffix(r.HumpPackageName, "test") { + r.HumpPackageName = r.HumpPackageName + "_" + } // test + length := len(r.Fields) + dict := make(map[string]string, length) + r.DataSourceMap = make(map[string]*DataSource, length) + for i := 0; i < length; i++ { + if r.Fields[i].Excel { + r.HasExcel = true + } + if r.Fields[i].DictType != "" { + dict[r.Fields[i].DictType] = "" + } + if r.Fields[i].Sort { + r.NeedSort = true + } + switch r.Fields[i].FieldType { + case "file": + r.HasFile = true + r.NeedJSON = true + case "json": + r.NeedJSON = true + case "array": + r.NeedJSON = true + r.HasArray = true + case "video": + r.HasPic = true + case "richtext": + r.HasRichText = true + case "picture": + r.HasPic = true + case "pictures": + r.HasPic = true + r.NeedJSON = true + case "time.Time": + r.HasTimer = true + if r.Fields[i].FieldSearchType != "" { + r.HasSearchTimer = true + } + } + if r.Fields[i].DataSource != nil { + if r.Fields[i].DataSource.Table != "" && r.Fields[i].DataSource.Label != "" && r.Fields[i].DataSource.Value != "" { + r.HasDataSource = true + r.Fields[i].CheckDataSource = true + r.DataSourceMap[r.Fields[i].FieldJson] = r.Fields[i].DataSource + } + } + if !r.GvaModel && r.PrimaryField == nil && r.Fields[i].PrimaryKey { + r.PrimaryField = r.Fields[i] + } // 自定义主键 + } + { + for key := range dict { + r.DictTypes = append(r.DictTypes, key) + } + } // DictTypes => 字典 + { + if r.GvaModel { + r.PrimaryField = &AutoCodeField{ + FieldName: "ID", + FieldType: "uint", + FieldDesc: "ID", + FieldJson: "ID", + DataTypeLong: "20", + Comment: "主键ID", + ColumnName: "id", + } + } + } // GvaModel + if r.Package == "" { + return errors.New("Package为空!") + } // 增加判断:Package不为空 + packages := []rune(r.Package) + if len(packages) > 0 { + if packages[0] >= 97 && packages[0] <= 122 { + packages[0] = packages[0] - 32 + } + r.PackageT = string(packages) + } // PackageT 是 Package 的首字母大写 + return nil +} + +func (r *AutoCode) History() SysAutoHistoryCreate { + bytes, _ := json.Marshal(r) + return SysAutoHistoryCreate{ + Table: r.TableName, + Package: r.Package, + Request: string(bytes), + StructName: r.StructName, + BusinessDB: r.BusinessDB, + Description: r.Description, + } +} + +type AutoCodeField struct { + FieldName string `json:"fieldName"` // Field名 + FieldDesc string `json:"fieldDesc"` // 中文名 + FieldType string `json:"fieldType"` // Field数据类型 + FieldJson string `json:"fieldJson"` // FieldJson + DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度 + Comment string `json:"comment"` // 数据库字段描述 + ColumnName string `json:"columnName"` // 数据库字段 + FieldSearchType string `json:"fieldSearchType"` // 搜索条件 + FieldSearchHide bool `json:"fieldSearchHide"` // 是否隐藏查询条件 + DictType string `json:"dictType"` // 字典 + //Front bool `json:"front"` // 是否前端可见 + Form bool `json:"form"` // 是否前端新建/编辑 + Table bool `json:"table"` // 是否前端表格列 + Desc bool `json:"desc"` // 是否前端详情 + Excel bool `json:"excel"` // 是否导入/导出 + Require bool `json:"require"` // 是否必填 + DefaultValue string `json:"defaultValue"` // 是否必填 + ErrorText string `json:"errorText"` // 校验失败文字 + Clearable bool `json:"clearable"` // 是否可清空 + Sort bool `json:"sort"` // 是否增加排序 + PrimaryKey bool `json:"primaryKey"` // 是否主键 + DataSource *DataSource `json:"dataSource"` // 数据源 + CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源 + FieldIndexType string `json:"fieldIndexType"` // 索引类型 +} + +type AutoFunc struct { + Package string `json:"package"` + FuncName string `json:"funcName"` // 方法名称 + Router string `json:"router"` // 路由名称 + FuncDesc string `json:"funcDesc"` // 方法介绍 + BusinessDB string `json:"businessDB"` // 业务库 + StructName string `json:"structName"` // Struct名称 + PackageName string `json:"packageName"` // 文件名称 + Description string `json:"description"` // Struct中文名称 + Abbreviation string `json:"abbreviation"` // Struct简称 + HumpPackageName string `json:"humpPackageName"` // go文件名称 + Method string `json:"method"` // 方法 + IsPlugin bool `json:"isPlugin"` // 是否插件 + IsAuth bool `json:"isAuth"` // 是否鉴权 +} + +type InitMenu struct { + PlugName string `json:"plugName"` + ParentMenu string `json:"parentMenu"` + Menus []uint `json:"menus"` +} + +type InitApi struct { + PlugName string `json:"plugName"` + APIs []uint `json:"apis"` +} + +type LLMAutoCode struct { + Prompt string `json:"prompt" form:"prompt" gorm:"column:prompt;comment:提示语;type:text;"` //提示语 + Mode string `json:"mode" form:"mode" gorm:"column:mode;comment:模式;type:text;"` //模式 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_code_package.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_code_package.go new file mode 100644 index 000000000..8494cb178 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_code_package.go @@ -0,0 +1,27 @@ +package request + +import ( + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type SysAutoCodePackageCreate struct { + Desc string `json:"desc" example:"描述"` + Label string `json:"label" example:"展示名"` + Template string `json:"template" example:"模版"` + PackageName string `json:"packageName" example:"包名"` +} + +func (r *SysAutoCodePackageCreate) AutoCode() AutoCode { + return AutoCode{ + Package: r.PackageName, + } +} + +func (r *SysAutoCodePackageCreate) Create() model.SysAutoCodePackage { + return model.SysAutoCodePackage{ + Desc: r.Desc, + Label: r.Label, + Template: r.Template, + PackageName: r.PackageName, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_history.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_history.go new file mode 100644 index 000000000..fb50a7944 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_auto_history.go @@ -0,0 +1,56 @@ +package request + +import ( + common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type SysAutoHistoryCreate struct { + Table string // 表名 + Package string // 模块名/插件名 + Request string // 前端传入的结构化信息 + StructName string // 结构体名称 + BusinessDB string // 业务库 + Description string // Struct中文名称 + Injections map[string]string // 注入路径 + Templates map[string]string // 模板信息 + ApiIDs []uint // api表注册内容 + MenuID uint // 菜单ID + ExportTemplateID uint // 导出模板ID +} + +func (r *SysAutoHistoryCreate) Create() model.SysAutoCodeHistory { + entity := model.SysAutoCodeHistory{ + Package: r.Package, + Request: r.Request, + Table: r.Table, + StructName: r.StructName, + BusinessDB: r.BusinessDB, + Description: r.Description, + Injections: r.Injections, + Templates: r.Templates, + ApiIDs: r.ApiIDs, + MenuID: r.MenuID, + ExportTemplateID: r.ExportTemplateID, + } + if entity.Table == "" { + entity.Table = r.StructName + } + return entity +} + +type SysAutoHistoryRollBack struct { + common.GetById + DeleteApi bool `json:"deleteApi" form:"deleteApi"` // 是否删除接口 + DeleteMenu bool `json:"deleteMenu" form:"deleteMenu"` // 是否删除菜单 + DeleteTable bool `json:"deleteTable" form:"deleteTable"` // 是否删除表 +} + +func (r *SysAutoHistoryRollBack) ApiIds(entity model.SysAutoCodeHistory) common.IdsReq { + length := len(entity.ApiIDs) + ids := make([]int, 0) + for i := 0; i < length; i++ { + ids = append(ids, int(entity.ApiIDs[i])) + } + return common.IdsReq{Ids: ids} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_casbin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_casbin.go new file mode 100644 index 000000000..ef8c823cb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_casbin.go @@ -0,0 +1,27 @@ +package request + +// Casbin info structure +type CasbinInfo struct { + Path string `json:"path"` // 路径 + Method string `json:"method"` // 方法 +} + +// Casbin structure for input parameters +type CasbinInReceive struct { + AuthorityId uint `json:"authorityId"` // 权限id + CasbinInfos []CasbinInfo `json:"casbinInfos"` +} + +func DefaultCasbin() []CasbinInfo { + return []CasbinInfo{ + {Path: "/menu/getMenu", Method: "POST"}, + {Path: "/jwt/jsonInBlacklist", Method: "POST"}, + {Path: "/base/login", Method: "POST"}, + {Path: "/user/changePassword", Method: "POST"}, + {Path: "/user/setUserAuthority", Method: "POST"}, + {Path: "/user/getUserInfo", Method: "GET"}, + {Path: "/user/setSelfInfo", Method: "PUT"}, + {Path: "/fileUploadAndDownload/upload", Method: "POST"}, + {Path: "/sysDictionary/findSysDictionary", Method: "GET"}, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_dictionary_detail.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_dictionary_detail.go new file mode 100644 index 000000000..2f97da280 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_dictionary_detail.go @@ -0,0 +1,11 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type SysDictionaryDetailSearch struct { + system.SysDictionaryDetail + request.PageInfo +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_export_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_export_template.go new file mode 100644 index 000000000..1010bf6b9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_export_template.go @@ -0,0 +1,14 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "time" +) + +type SysExportTemplateSearch struct { + system.SysExportTemplate + StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"` + EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"` + request.PageInfo +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_init.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_init.go new file mode 100644 index 000000000..6882895d2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_init.go @@ -0,0 +1,123 @@ +package request + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/config" + "os" +) + +type InitDB struct { + AdminPassword string `json:"adminPassword" binding:"required"` + DBType string `json:"dbType"` // 数据库类型 + Host string `json:"host"` // 服务器地址 + Port string `json:"port"` // 数据库连接端口 + UserName string `json:"userName"` // 数据库用户名 + Password string `json:"password"` // 数据库密码 + DBName string `json:"dbName" binding:"required"` // 数据库名 + DBPath string `json:"dbPath"` // sqlite数据库文件路径 +} + +// MysqlEmptyDsn msyql 空数据库 建库链接 +// Author SliverHorn +func (i *InitDB) MysqlEmptyDsn() string { + if i.Host == "" { + i.Host = "127.0.0.1" + } + if i.Port == "" { + i.Port = "3306" + } + return fmt.Sprintf("%s:%s@tcp(%s:%s)/", i.UserName, i.Password, i.Host, i.Port) +} + +// PgsqlEmptyDsn pgsql 空数据库 建库链接 +// Author SliverHorn +func (i *InitDB) PgsqlEmptyDsn() string { + if i.Host == "" { + i.Host = "127.0.0.1" + } + if i.Port == "" { + i.Port = "5432" + } + return "host=" + i.Host + " user=" + i.UserName + " password=" + i.Password + " port=" + i.Port + " dbname=" + "postgres" + " " + "sslmode=disable TimeZone=Asia/Shanghai" +} + +// SqliteEmptyDsn sqlite 空数据库 建库链接 +// Author Kafumio +func (i *InitDB) SqliteEmptyDsn() string { + separator := string(os.PathSeparator) + return i.DBPath + separator + i.DBName + ".db" +} + +func (i *InitDB) MssqlEmptyDsn() string { + return "sqlserver://" + i.UserName + ":" + i.Password + "@" + i.Host + ":" + i.Port + "?database=" + i.DBName + "&encrypt=disable" +} + +// ToMysqlConfig 转换 config.Mysql +// Author [SliverHorn](https://github.com/SliverHorn) +func (i *InitDB) ToMysqlConfig() config.Mysql { + return config.Mysql{ + GeneralDB: config.GeneralDB{ + Path: i.Host, + Port: i.Port, + Dbname: i.DBName, + Username: i.UserName, + Password: i.Password, + MaxIdleConns: 10, + MaxOpenConns: 100, + LogMode: "error", + Config: "charset=utf8mb4&parseTime=True&loc=Local", + }, + } +} + +// ToPgsqlConfig 转换 config.Pgsql +// Author [SliverHorn](https://github.com/SliverHorn) +func (i *InitDB) ToPgsqlConfig() config.Pgsql { + return config.Pgsql{ + GeneralDB: config.GeneralDB{ + Path: i.Host, + Port: i.Port, + Dbname: i.DBName, + Username: i.UserName, + Password: i.Password, + MaxIdleConns: 10, + MaxOpenConns: 100, + LogMode: "error", + Config: "sslmode=disable TimeZone=Asia/Shanghai", + }, + } +} + +// ToSqliteConfig 转换 config.Sqlite +// Author [Kafumio](https://github.com/Kafumio) +func (i *InitDB) ToSqliteConfig() config.Sqlite { + return config.Sqlite{ + GeneralDB: config.GeneralDB{ + Path: i.DBPath, + Port: i.Port, + Dbname: i.DBName, + Username: i.UserName, + Password: i.Password, + MaxIdleConns: 10, + MaxOpenConns: 100, + LogMode: "error", + Config: "", + }, + } +} + +func (i *InitDB) ToMssqlConfig() config.Mssql { + return config.Mssql{ + GeneralDB: config.GeneralDB{ + Path: i.DBPath, + Port: i.Port, + Dbname: i.DBName, + Username: i.UserName, + Password: i.Password, + MaxIdleConns: 10, + MaxOpenConns: 100, + LogMode: "error", + Config: "", + }, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_menu.go new file mode 100644 index 000000000..2f5c7c46e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_menu.go @@ -0,0 +1,27 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +// Add menu authority info structure +type AddMenuAuthorityInfo struct { + Menus []system.SysBaseMenu `json:"menus"` + AuthorityId uint `json:"authorityId"` // 角色ID +} + +func DefaultMenu() []system.SysBaseMenu { + return []system.SysBaseMenu{{ + GVA_MODEL: global.GVA_MODEL{ID: 1}, + ParentId: 0, + Path: "dashboard", + Name: "dashboard", + Component: "view/dashboard/index.vue", + Sort: 1, + Meta: system.Meta{ + Title: "仪表盘", + Icon: "setting", + }, + }} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_operation_record.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_operation_record.go new file mode 100644 index 000000000..e58dd59c3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_operation_record.go @@ -0,0 +1,11 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type SysOperationRecordSearch struct { + system.SysOperationRecord + request.PageInfo +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_params.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_params.go new file mode 100644 index 000000000..0009271bf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_params.go @@ -0,0 +1,14 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "time" +) + +type SysParamsSearch struct { + StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"` + EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"` + Name string `json:"name" form:"name" ` + Key string `json:"key" form:"key" ` + request.PageInfo +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_user.go new file mode 100644 index 000000000..2e698d440 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/request/sys_user.go @@ -0,0 +1,65 @@ +package request + +import ( + common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +// Register User register structure +type Register struct { + Username string `json:"userName" example:"用户名"` + Password string `json:"passWord" example:"密码"` + NickName string `json:"nickName" example:"昵称"` + HeaderImg string `json:"headerImg" example:"头像链接"` + AuthorityId uint `json:"authorityId" swaggertype:"string" example:"int 角色id"` + Enable int `json:"enable" swaggertype:"string" example:"int 是否启用"` + AuthorityIds []uint `json:"authorityIds" swaggertype:"string" example:"[]uint 角色id"` + Phone string `json:"phone" example:"电话号码"` + Email string `json:"email" example:"电子邮箱"` +} + +// User login structure +type Login struct { + Username string `json:"username"` // 用户名 + Password string `json:"password"` // 密码 + Captcha string `json:"captcha"` // 验证码 + CaptchaId string `json:"captchaId"` // 验证码ID +} + +// Modify password structure +type ChangePasswordReq struct { + ID uint `json:"-"` // 从 JWT 中提取 user id,避免越权 + Password string `json:"password"` // 密码 + NewPassword string `json:"newPassword"` // 新密码 +} + +// Modify user's auth structure +type SetUserAuth struct { + AuthorityId uint `json:"authorityId"` // 角色ID +} + +// Modify user's auth structure +type SetUserAuthorities struct { + ID uint + AuthorityIds []uint `json:"authorityIds"` // 角色ID +} + +type ChangeUserInfo struct { + ID uint `gorm:"primarykey"` // 主键ID + NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 + Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户手机号 + AuthorityIds []uint `json:"authorityIds" gorm:"-"` // 角色ID + Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱 + HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像 + SideMode string `json:"sideMode" gorm:"comment:用户侧边主题"` // 用户侧边主题 + Enable int `json:"enable" gorm:"comment:冻结用户"` //冻结用户 + Authorities []system.SysAuthority `json:"-" gorm:"many2many:sys_user_authority;"` +} + +type GetUserList struct { + common.PageInfo + Username string `json:"username" form:"username"` + NickName string `json:"nickName" form:"nickName"` + Phone string `json:"phone" form:"phone"` + Email string `json:"email" form:"email"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_api.go new file mode 100644 index 000000000..20e382b9a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_api.go @@ -0,0 +1,18 @@ +package response + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type SysAPIResponse struct { + Api system.SysApi `json:"api"` +} + +type SysAPIListResponse struct { + Apis []system.SysApi `json:"apis"` +} + +type SysSyncApis struct { + NewApis []system.SysApi `json:"newApis"` + DeleteApis []system.SysApi `json:"deleteApis"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_authority.go new file mode 100644 index 000000000..a05540167 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_authority.go @@ -0,0 +1,12 @@ +package response + +import "github.com/flipped-aurora/gin-vue-admin/server/model/system" + +type SysAuthorityResponse struct { + Authority system.SysAuthority `json:"authority"` +} + +type SysAuthorityCopyResponse struct { + Authority system.SysAuthority `json:"authority"` + OldAuthorityId uint `json:"oldAuthorityId"` // 旧角色ID +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_authority_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_authority_btn.go new file mode 100644 index 000000000..2f772cf09 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_authority_btn.go @@ -0,0 +1,5 @@ +package response + +type SysAuthorityBtnRes struct { + Selected []uint `json:"selected"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_auto_code.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_auto_code.go new file mode 100644 index 000000000..9e99bde3a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_auto_code.go @@ -0,0 +1,17 @@ +package response + +type Db struct { + Database string `json:"database" gorm:"column:database"` +} + +type Table struct { + TableName string `json:"tableName" gorm:"column:table_name"` +} + +type Column struct { + DataType string `json:"dataType" gorm:"column:data_type"` + ColumnName string `json:"columnName" gorm:"column:column_name"` + DataTypeLong string `json:"dataTypeLong" gorm:"column:data_type_long"` + ColumnComment string `json:"columnComment" gorm:"column:column_comment"` + PrimaryKey bool `json:"primaryKey" gorm:"column:primary_key"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_captcha.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_captcha.go new file mode 100644 index 000000000..0c3995a1d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_captcha.go @@ -0,0 +1,8 @@ +package response + +type SysCaptchaResponse struct { + CaptchaId string `json:"captchaId"` + PicPath string `json:"picPath"` + CaptchaLength int `json:"captchaLength"` + OpenCaptcha bool `json:"openCaptcha"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_casbin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_casbin.go new file mode 100644 index 000000000..267bb42c9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_casbin.go @@ -0,0 +1,9 @@ +package response + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" +) + +type PolicyPathResponse struct { + Paths []request.CasbinInfo `json:"paths"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_menu.go new file mode 100644 index 000000000..d8f80f314 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_menu.go @@ -0,0 +1,15 @@ +package response + +import "github.com/flipped-aurora/gin-vue-admin/server/model/system" + +type SysMenusResponse struct { + Menus []system.SysMenu `json:"menus"` +} + +type SysBaseMenusResponse struct { + Menus []system.SysBaseMenu `json:"menus"` +} + +type SysBaseMenuResponse struct { + Menu system.SysBaseMenu `json:"menu"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_system.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_system.go new file mode 100644 index 000000000..f19e965bf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_system.go @@ -0,0 +1,7 @@ +package response + +import "github.com/flipped-aurora/gin-vue-admin/server/config" + +type SysConfigResponse struct { + Config config.Server `json:"config"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_user.go new file mode 100644 index 000000000..d6f1074b7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/response/sys_user.go @@ -0,0 +1,15 @@ +package response + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +type SysUserResponse struct { + User system.SysUser `json:"user"` +} + +type LoginResponse struct { + User system.SysUser `json:"user"` + Token string `json:"token"` + ExpiresAt int64 `json:"expiresAt"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_api.go new file mode 100644 index 000000000..853ddb087 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_api.go @@ -0,0 +1,28 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +type SysApi struct { + global.GVA_MODEL + Path string `json:"path" gorm:"comment:api路径"` // api路径 + Description string `json:"description" gorm:"comment:api中文描述"` // api中文描述 + ApiGroup string `json:"apiGroup" gorm:"comment:api组"` // api组 + Method string `json:"method" gorm:"default:POST;comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE +} + +func (SysApi) TableName() string { + return "sys_apis" +} + +type SysIgnoreApi struct { + global.GVA_MODEL + Path string `json:"path" gorm:"comment:api路径"` // api路径 + Method string `json:"method" gorm:"default:POST;comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE + Flag bool `json:"flag" gorm:"-"` // 是否忽略 +} + +func (SysIgnoreApi) TableName() string { + return "sys_ignore_apis" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority.go new file mode 100644 index 000000000..01c5efad1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority.go @@ -0,0 +1,23 @@ +package system + +import ( + "time" +) + +type SysAuthority struct { + CreatedAt time.Time // 创建时间 + UpdatedAt time.Time // 更新时间 + DeletedAt *time.Time `sql:"index"` + AuthorityId uint `json:"authorityId" gorm:"not null;unique;primary_key;comment:角色ID;size:90"` // 角色ID + AuthorityName string `json:"authorityName" gorm:"comment:角色名"` // 角色名 + ParentId *uint `json:"parentId" gorm:"comment:父角色ID"` // 父角色ID + DataAuthorityId []*SysAuthority `json:"dataAuthorityId" gorm:"many2many:sys_data_authority_id;"` + Children []SysAuthority `json:"children" gorm:"-"` + SysBaseMenus []SysBaseMenu `json:"menus" gorm:"many2many:sys_authority_menus;"` + Users []SysUser `json:"-" gorm:"many2many:sys_user_authority;"` + DefaultRouter string `json:"defaultRouter" gorm:"comment:默认菜单;default:dashboard"` // 默认菜单(默认dashboard) +} + +func (SysAuthority) TableName() string { + return "sys_authorities" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority_btn.go new file mode 100644 index 000000000..e00598412 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority_btn.go @@ -0,0 +1,8 @@ +package system + +type SysAuthorityBtn struct { + AuthorityId uint `gorm:"comment:角色ID"` + SysMenuID uint `gorm:"comment:菜单ID"` + SysBaseMenuBtnID uint `gorm:"comment:菜单按钮ID"` + SysBaseMenuBtn SysBaseMenuBtn ` gorm:"comment:按钮详情"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority_menu.go new file mode 100644 index 000000000..4467a7e10 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_authority_menu.go @@ -0,0 +1,19 @@ +package system + +type SysMenu struct { + SysBaseMenu + MenuId uint `json:"menuId" gorm:"comment:菜单ID"` + AuthorityId uint `json:"-" gorm:"comment:角色ID"` + Children []SysMenu `json:"children" gorm:"-"` + Parameters []SysBaseMenuParameter `json:"parameters" gorm:"foreignKey:SysBaseMenuID;references:MenuId"` + Btns map[string]uint `json:"btns" gorm:"-"` +} + +type SysAuthorityMenu struct { + MenuId string `json:"menuId" gorm:"comment:菜单ID;column:sys_base_menu_id"` + AuthorityId string `json:"-" gorm:"comment:角色ID;column:sys_authority_authority_id"` +} + +func (s SysAuthorityMenu) TableName() string { + return "sys_authority_menus" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_auto_code_history.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_auto_code_history.go new file mode 100644 index 000000000..c36787ddb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_auto_code_history.go @@ -0,0 +1,67 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "gorm.io/gorm" + "os" + "path" + "path/filepath" + "strings" +) + +// SysAutoCodeHistory 自动迁移代码记录,用于回滚,重放使用 +type SysAutoCodeHistory struct { + global.GVA_MODEL + Table string `json:"tableName" gorm:"column:table_name;comment:表名"` + Package string `json:"package" gorm:"column:package;comment:模块名/插件名"` + Request string `json:"request" gorm:"type:text;column:request;comment:前端传入的结构化信息"` + StructName string `json:"structName" gorm:"column:struct_name;comment:结构体名称"` + BusinessDB string `json:"businessDb" gorm:"column:business_db;comment:业务库"` + Description string `json:"description" gorm:"column:description;comment:Struct中文名称"` + Templates map[string]string `json:"template" gorm:"serializer:json;type:text;column:templates;comment:模板信息"` + Injections map[string]string `json:"injections" gorm:"serializer:json;type:text;column:Injections;comment:注入路径"` + Flag int `json:"flag" gorm:"column:flag;comment:[0:创建,1:回滚]"` + ApiIDs []uint `json:"apiIDs" gorm:"serializer:json;column:api_ids;comment:api表注册内容"` + MenuID uint `json:"menuId" gorm:"column:menu_id;comment:菜单ID"` + ExportTemplateID uint `json:"exportTemplateID" gorm:"column:export_template_id;comment:导出模板ID"` + AutoCodePackage SysAutoCodePackage `json:"autoCodePackage" gorm:"foreignKey:ID;references:PackageID"` + PackageID uint `json:"packageID" gorm:"column:package_id;comment:包ID"` +} + +func (s *SysAutoCodeHistory) BeforeCreate(db *gorm.DB) error { + templates := make(map[string]string, len(s.Templates)) + for key, value := range s.Templates { + server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + { + hasServer := strings.Index(key, server) + if hasServer != -1 { + key = strings.TrimPrefix(key, server) + keys := strings.Split(key, string(os.PathSeparator)) + key = path.Join(keys...) + } + } // key + web := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot()) + hasWeb := strings.Index(value, web) + if hasWeb != -1 { + value = strings.TrimPrefix(value, web) + values := strings.Split(value, string(os.PathSeparator)) + value = path.Join(values...) + templates[key] = value + continue + } + hasServer := strings.Index(value, server) + if hasServer != -1 { + value = strings.TrimPrefix(value, server) + values := strings.Split(value, string(os.PathSeparator)) + value = path.Join(values...) + templates[key] = value + continue + } + } + s.Templates = templates + return nil +} + +func (s *SysAutoCodeHistory) TableName() string { + return "sys_auto_code_histories" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_auto_code_package.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_auto_code_package.go new file mode 100644 index 000000000..e87e88538 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_auto_code_package.go @@ -0,0 +1,17 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +type SysAutoCodePackage struct { + global.GVA_MODEL + Desc string `json:"desc" gorm:"comment:描述"` + Label string `json:"label" gorm:"comment:展示名"` + Template string `json:"template" gorm:"comment:模版"` + PackageName string `json:"packageName" gorm:"comment:包名"` +} + +func (s *SysAutoCodePackage) TableName() string { + return "sys_auto_code_packages" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_base_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_base_menu.go new file mode 100644 index 000000000..41cf37631 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_base_menu.go @@ -0,0 +1,42 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +type SysBaseMenu struct { + global.GVA_MODEL + MenuLevel uint `json:"-"` + ParentId uint `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID + Path string `json:"path" gorm:"comment:路由path"` // 路由path + Name string `json:"name" gorm:"comment:路由name"` // 路由name + Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏 + Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径 + Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记 + Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性 + SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"` + Children []SysBaseMenu `json:"children" gorm:"-"` + Parameters []SysBaseMenuParameter `json:"parameters"` + MenuBtn []SysBaseMenuBtn `json:"menuBtn"` +} + +type Meta struct { + ActiveName string `json:"activeName" gorm:"comment:高亮菜单"` + KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存 + DefaultMenu bool `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中) + Title string `json:"title" gorm:"comment:菜单名"` // 菜单名 + Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标 + CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab +} + +type SysBaseMenuParameter struct { + global.GVA_MODEL + SysBaseMenuID uint + Type string `json:"type" gorm:"comment:地址栏携带参数为params还是query"` // 地址栏携带参数为params还是query + Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key + Value string `json:"value" gorm:"comment:地址栏携带参数的值"` // 地址栏携带参数的值 +} + +func (SysBaseMenu) TableName() string { + return "sys_base_menus" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_dictionary.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_dictionary.go new file mode 100644 index 000000000..c0b9bf7fc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_dictionary.go @@ -0,0 +1,20 @@ +// 自动生成模板SysDictionary +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// 如果含有time.Time 请自行import time包 +type SysDictionary struct { + global.GVA_MODEL + Name string `json:"name" form:"name" gorm:"column:name;comment:字典名(中)"` // 字典名(中) + Type string `json:"type" form:"type" gorm:"column:type;comment:字典名(英)"` // 字典名(英) + Status *bool `json:"status" form:"status" gorm:"column:status;comment:状态"` // 状态 + Desc string `json:"desc" form:"desc" gorm:"column:desc;comment:描述"` // 描述 + SysDictionaryDetails []SysDictionaryDetail `json:"sysDictionaryDetails" form:"sysDictionaryDetails"` +} + +func (SysDictionary) TableName() string { + return "sys_dictionaries" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_dictionary_detail.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_dictionary_detail.go new file mode 100644 index 000000000..4084136c2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_dictionary_detail.go @@ -0,0 +1,21 @@ +// 自动生成模板SysDictionaryDetail +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// 如果含有time.Time 请自行import time包 +type SysDictionaryDetail struct { + global.GVA_MODEL + Label string `json:"label" form:"label" gorm:"column:label;comment:展示值"` // 展示值 + Value string `json:"value" form:"value" gorm:"column:value;comment:字典值"` // 字典值 + Extend string `json:"extend" form:"extend" gorm:"column:extend;comment:扩展值"` // 扩展值 + Status *bool `json:"status" form:"status" gorm:"column:status;comment:启用状态"` // 启用状态 + Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序标记"` // 排序标记 + SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" gorm:"column:sys_dictionary_id;comment:关联标记"` // 关联标记 +} + +func (SysDictionaryDetail) TableName() string { + return "sys_dictionary_details" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_export_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_export_template.go new file mode 100644 index 000000000..aef24617d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_export_template.go @@ -0,0 +1,44 @@ +// 自动生成模板SysExportTemplate +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// 导出模板 结构体 SysExportTemplate +type SysExportTemplate struct { + global.GVA_MODEL + DBName string `json:"dbName" form:"dbName" gorm:"column:db_name;comment:数据库名称;"` //数据库名称 + Name string `json:"name" form:"name" gorm:"column:name;comment:模板名称;"` //模板名称 + TableName string `json:"tableName" form:"tableName" gorm:"column:table_name;comment:表名称;"` //表名称 + TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识;"` //模板标识 + TemplateInfo string `json:"templateInfo" form:"templateInfo" gorm:"column:template_info;type:text;"` //模板信息 + Limit *int `json:"limit" form:"limit" gorm:"column:limit;comment:导出限制"` + Order string `json:"order" form:"order" gorm:"column:order;comment:排序"` + Conditions []Condition `json:"conditions" form:"conditions" gorm:"foreignKey:TemplateID;references:TemplateID;comment:条件"` + JoinTemplate []JoinTemplate `json:"joinTemplate" form:"joinTemplate" gorm:"foreignKey:TemplateID;references:TemplateID;comment:关联"` +} + +type JoinTemplate struct { + global.GVA_MODEL + TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"` + JOINS string `json:"joins" form:"joins" gorm:"column:joins;comment:关联"` + Table string `json:"table" form:"table" gorm:"column:table;comment:关联表"` + ON string `json:"on" form:"on" gorm:"column:on;comment:关联条件"` +} + +func (JoinTemplate) TableName() string { + return "sys_export_template_join" +} + +type Condition struct { + global.GVA_MODEL + TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"` + From string `json:"from" form:"from" gorm:"column:from;comment:条件取的key"` + Column string `json:"column" form:"column" gorm:"column:column;comment:作为查询条件的字段"` + Operator string `json:"operator" form:"operator" gorm:"column:operator;comment:操作符"` +} + +func (Condition) TableName() string { + return "sys_export_template_condition" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_jwt_blacklist.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_jwt_blacklist.go new file mode 100644 index 000000000..4f9fa396d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_jwt_blacklist.go @@ -0,0 +1,10 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +type JwtBlacklist struct { + global.GVA_MODEL + Jwt string `gorm:"type:text;comment:jwt"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_menu_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_menu_btn.go new file mode 100644 index 000000000..9d3276142 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_menu_btn.go @@ -0,0 +1,10 @@ +package system + +import "github.com/flipped-aurora/gin-vue-admin/server/global" + +type SysBaseMenuBtn struct { + global.GVA_MODEL + Name string `json:"name" gorm:"comment:按钮关键key"` + Desc string `json:"desc" gorm:"按钮备注"` + SysBaseMenuID uint `json:"sysBaseMenuID" gorm:"comment:菜单ID"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_operation_record.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_operation_record.go new file mode 100644 index 000000000..3d201d30d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_operation_record.go @@ -0,0 +1,24 @@ +// 自动生成模板SysOperationRecord +package system + +import ( + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// 如果含有time.Time 请自行import time包 +type SysOperationRecord struct { + global.GVA_MODEL + Ip string `json:"ip" form:"ip" gorm:"column:ip;comment:请求ip"` // 请求ip + Method string `json:"method" form:"method" gorm:"column:method;comment:请求方法"` // 请求方法 + Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径 + Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态 + Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟 + Agent string `json:"agent" form:"agent" gorm:"type:text;column:agent;comment:代理"` // 代理 + ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息 + Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body + Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body + UserID int `json:"user_id" form:"user_id" gorm:"column:user_id;comment:用户id"` // 用户id + User SysUser `json:"user"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_params.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_params.go new file mode 100644 index 000000000..049c07f20 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_params.go @@ -0,0 +1,20 @@ +// 自动生成模板SysParams +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// 参数 结构体 SysParams +type SysParams struct { + global.GVA_MODEL + Name string `json:"name" form:"name" gorm:"column:name;comment:参数名称;" binding:"required"` //参数名称 + Key string `json:"key" form:"key" gorm:"column:key;comment:参数键;" binding:"required"` //参数键 + Value string `json:"value" form:"value" gorm:"column:value;comment:参数值;" binding:"required"` //参数值 + Desc string `json:"desc" form:"desc" gorm:"column:desc;comment:参数说明;"` //参数说明 +} + +// TableName 参数 SysParams自定义表名 sys_params +func (SysParams) TableName() string { + return "sys_params" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_system.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_system.go new file mode 100644 index 000000000..ad983110b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_system.go @@ -0,0 +1,10 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" +) + +// 配置文件结构体 +type System struct { + Config config.Server `json:"config"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_user.go new file mode 100644 index 000000000..916c6b6e0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_user.go @@ -0,0 +1,62 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common" + "github.com/gofrs/uuid/v5" +) + +type Login interface { + GetUsername() string + GetNickname() string + GetUUID() uuid.UUID + GetUserId() uint + GetAuthorityId() uint + GetUserInfo() any +} + +var _ Login = new(SysUser) + +type SysUser struct { + global.GVA_MODEL + UUID uuid.UUID `json:"uuid" gorm:"index;comment:用户UUID"` // 用户UUID + Username string `json:"userName" gorm:"index;comment:用户登录名"` // 用户登录名 + Password string `json:"-" gorm:"comment:用户登录密码"` // 用户登录密码 + NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 + HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像 + AuthorityId uint `json:"authorityId" gorm:"default:888;comment:用户角色ID"` // 用户角色ID + Authority SysAuthority `json:"authority" gorm:"foreignKey:AuthorityId;references:AuthorityId;comment:用户角色"` // 用户角色 + Authorities []SysAuthority `json:"authorities" gorm:"many2many:sys_user_authority;"` // 多用户角色 + Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户手机号 + Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱 + Enable int `json:"enable" gorm:"default:1;comment:用户是否被冻结 1正常 2冻结"` //用户是否被冻结 1正常 2冻结 + OriginSetting common.JSONMap `json:"originSetting" form:"originSetting" gorm:"type:text;default:null;column:origin_setting;comment:配置;"` //配置 +} + +func (SysUser) TableName() string { + return "sys_users" +} + +func (s *SysUser) GetUsername() string { + return s.Username +} + +func (s *SysUser) GetNickname() string { + return s.NickName +} + +func (s *SysUser) GetUUID() uuid.UUID { + return s.UUID +} + +func (s *SysUser) GetUserId() uint { + return s.ID +} + +func (s *SysUser) GetAuthorityId() uint { + return s.AuthorityId +} + +func (s *SysUser) GetUserInfo() any { + return *s +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_user_authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_user_authority.go new file mode 100644 index 000000000..1aa83cbd5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/model/system/sys_user_authority.go @@ -0,0 +1,11 @@ +package system + +// SysUserAuthority 是 sysUser 和 sysAuthority 的连接表 +type SysUserAuthority struct { + SysUserId uint `gorm:"column:sys_user_id"` + SysAuthorityAuthorityId uint `gorm:"column:sys_authority_authority_id"` +} + +func (s *SysUserAuthority) TableName() string { + return "sys_user_authority" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/api/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/api/enter.go new file mode 100644 index 000000000..7fee6fc2b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/api/enter.go @@ -0,0 +1,10 @@ +package api + +import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/service" + +var ( + Api = new(api) + serviceInfo = service.Service.Info +) + +type api struct{ Info info } diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/api/info.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/api/info.go new file mode 100644 index 000000000..dd0faa350 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/api/info.go @@ -0,0 +1,183 @@ +package api + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +var Info = new(info) + +type info struct{} + +// CreateInfo 创建公告 +// @Tags Info +// @Summary 创建公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Info true "创建公告" +// @Success 200 {object} response.Response{msg=string} "创建成功" +// @Router /info/createInfo [post] +func (a *info) CreateInfo(c *gin.Context) { + var info model.Info + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = serviceInfo.CreateInfo(&info) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + return + } + response.OkWithMessage("创建成功", c) +} + +// DeleteInfo 删除公告 +// @Tags Info +// @Summary 删除公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Info true "删除公告" +// @Success 200 {object} response.Response{msg=string} "删除成功" +// @Router /info/deleteInfo [delete] +func (a *info) DeleteInfo(c *gin.Context) { + ID := c.Query("ID") + err := serviceInfo.DeleteInfo(ID) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + return + } + response.OkWithMessage("删除成功", c) +} + +// DeleteInfoByIds 批量删除公告 +// @Tags Info +// @Summary 批量删除公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "批量删除成功" +// @Router /info/deleteInfoByIds [delete] +func (a *info) DeleteInfoByIds(c *gin.Context) { + IDs := c.QueryArray("IDs[]") + if err := serviceInfo.DeleteInfoByIds(IDs); err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败", c) + return + } + response.OkWithMessage("批量删除成功", c) +} + +// UpdateInfo 更新公告 +// @Tags Info +// @Summary 更新公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Info true "更新公告" +// @Success 200 {object} response.Response{msg=string} "更新成功" +// @Router /info/updateInfo [put] +func (a *info) UpdateInfo(c *gin.Context) { + var info model.Info + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = serviceInfo.UpdateInfo(info) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + return + } + response.OkWithMessage("更新成功", c) +} + +// FindInfo 用id查询公告 +// @Tags Info +// @Summary 用id查询公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.Info true "用id查询公告" +// @Success 200 {object} response.Response{data=model.Info,msg=string} "查询成功" +// @Router /info/findInfo [get] +func (a *info) FindInfo(c *gin.Context) { + ID := c.Query("ID") + reinfo, err := serviceInfo.GetInfo(ID) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + return + } + response.OkWithData(reinfo, c) +} + +// GetInfoList 分页获取公告列表 +// @Tags Info +// @Summary 分页获取公告列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.InfoSearch true "分页获取公告列表" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功" +// @Router /info/getInfoList [get] +func (a *info) GetInfoList(c *gin.Context) { + var pageInfo request.InfoSearch + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := serviceInfo.GetInfoInfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +// GetInfoDataSource 获取Info的数据源 +// @Tags Info +// @Summary 获取Info的数据源 +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=object,msg=string} "查询成功" +// @Router /info/getInfoDataSource [get] +func (a *info) GetInfoDataSource(c *gin.Context) { + // 此接口为获取数据源定义的数据 + dataSource, err := serviceInfo.GetInfoDataSource() + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + return + } + response.OkWithData(dataSource, c) +} + +// GetInfoPublic 不需要鉴权的公告接口 +// @Tags Info +// @Summary 不需要鉴权的公告接口 +// @accept application/json +// @Produce application/json +// @Param data query request.InfoSearch true "分页获取公告列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /info/getInfoPublic [get] +func (a *info) GetInfoPublic(c *gin.Context) { + // 此接口不需要鉴权 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑 + response.OkWithDetailed(gin.H{"info": "不需要鉴权的公告接口信息"}, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/config/config.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/config/config.go new file mode 100644 index 000000000..809bc990f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/config/config.go @@ -0,0 +1,4 @@ +package config + +type Config struct { +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/gen/gen.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/gen/gen.go new file mode 100644 index 000000000..240749ff1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/gen/gen.go @@ -0,0 +1,17 @@ +package main + +import ( + "gorm.io/gen" + "path/filepath" //go:generate go mod tidy + //go:generate go mod download + //go:generate go run gen.go + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model" +) + +func main() { + g := gen.NewGenerator(gen.Config{OutPath: filepath.Join("..", "..", "..", "announcement", "blender", "model", "dao"), Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface}) + g.ApplyBasic( + new(model.Info), + ) + g.Execute() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/api.go new file mode 100644 index 000000000..6d0fed1d0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/api.go @@ -0,0 +1,49 @@ +package initialize + +import ( + "context" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" +) + +func Api(ctx context.Context) { + entities := []model.SysApi{ + { + Path: "/info/createInfo", + Description: "新建公告", + ApiGroup: "公告", + Method: "POST", + }, + { + Path: "/info/deleteInfo", + Description: "删除公告", + ApiGroup: "公告", + Method: "DELETE", + }, + { + Path: "/info/deleteInfoByIds", + Description: "批量删除公告", + ApiGroup: "公告", + Method: "DELETE", + }, + { + Path: "/info/updateInfo", + Description: "更新公告", + ApiGroup: "公告", + Method: "PUT", + }, + { + Path: "/info/findInfo", + Description: "根据ID获取公告", + ApiGroup: "公告", + Method: "GET", + }, + { + Path: "/info/getInfoList", + Description: "获取公告列表", + ApiGroup: "公告", + Method: "GET", + }, + } + utils.RegisterApis(entities...) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/gorm.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/gorm.go new file mode 100644 index 000000000..3a88ff25a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/gorm.go @@ -0,0 +1,20 @@ +package initialize + +import ( + "context" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +func Gorm(ctx context.Context) { + err := global.GVA_DB.WithContext(ctx).AutoMigrate( + new(model.Info), + ) + if err != nil { + err = errors.Wrap(err, "注册表失败!") + zap.L().Error(fmt.Sprintf("%+v", err)) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/menu.go new file mode 100644 index 000000000..40aff2b50 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/menu.go @@ -0,0 +1,22 @@ +package initialize + +import ( + "context" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" +) + +func Menu(ctx context.Context) { + entities := []model.SysBaseMenu{ + { + ParentId: 24, + Path: "anInfo", + Name: "anInfo", + Hidden: false, + Component: "plugin/announcement/view/info.vue", + Sort: 5, + Meta: model.Meta{Title: "公告管理", Icon: "box"}, + }, + } + utils.RegisterMenus(entities...) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/router.go new file mode 100644 index 000000000..e2c4f1787 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/router.go @@ -0,0 +1,15 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/router" + "github.com/gin-gonic/gin" +) + +func Router(engine *gin.Engine) { + public := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("") + private := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("") + private.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) + router.Router.Info.Init(public, private) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/viper.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/viper.go new file mode 100644 index 000000000..68cfff685 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/initialize/viper.go @@ -0,0 +1,17 @@ +package initialize + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/plugin" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +func Viper() { + err := global.GVA_VP.UnmarshalKey("announcement", &plugin.Config) + if err != nil { + err = errors.Wrap(err, "初始化配置文件失败!") + zap.L().Error(fmt.Sprintf("%+v", err)) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/model/info.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/model/info.go new file mode 100644 index 000000000..fcaa11f59 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/model/info.go @@ -0,0 +1,20 @@ +package model + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "gorm.io/datatypes" +) + +// Info 公告 结构体 +type Info struct { + global.GVA_MODEL + Title string `json:"title" form:"title" gorm:"column:title;comment:公告标题;"` //标题 + Content string `json:"content" form:"content" gorm:"column:content;comment:公告内容;type:text;"` //内容 + UserID *int `json:"userID" form:"userID" gorm:"column:user_id;comment:发布者;"` //作者 + Attachments datatypes.JSON `json:"attachments" form:"attachments" gorm:"column:attachments;comment:相关附件;"swaggertype:"array,object"` //附件 +} + +// TableName 公告 Info自定义表名 gva_announcements_info +func (Info) TableName() string { + return "gva_announcements_info" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/model/request/info.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/model/request/info.go new file mode 100644 index 000000000..35be3e032 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/model/request/info.go @@ -0,0 +1,12 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "time" +) + +type InfoSearch struct { + StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"` + EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"` + request.PageInfo +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/plugin.go new file mode 100644 index 000000000..a20edb894 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/plugin.go @@ -0,0 +1,26 @@ +package announcement + +import ( + "context" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/initialize" + interfaces "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin/v2" + "github.com/gin-gonic/gin" +) + +var _ interfaces.Plugin = (*plugin)(nil) + +var Plugin = new(plugin) + +type plugin struct{} + +func (p *plugin) Register(group *gin.Engine) { + ctx := context.Background() + // 如果需要配置文件,请到config.Config中填充配置结构,且到下方发放中填入其在config.yaml中的key + // initialize.Viper() + // 安装插件时候自动注册的api数据请到下方法.Api方法中实现 + initialize.Api(ctx) + // 安装插件时候自动注册的api数据请到下方法.Menu方法中实现 + initialize.Menu(ctx) + initialize.Gorm(ctx) + initialize.Router(group) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/plugin/plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/plugin/plugin.go new file mode 100644 index 000000000..405823980 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/plugin/plugin.go @@ -0,0 +1,5 @@ +package plugin + +import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/config" + +var Config config.Config diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/router/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/router/enter.go new file mode 100644 index 000000000..543e0ffb2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/router/enter.go @@ -0,0 +1,10 @@ +package router + +import "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/api" + +var ( + Router = new(router) + apiInfo = api.Api.Info +) + +type router struct{ Info info } diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/router/info.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/router/info.go new file mode 100644 index 000000000..8de316b35 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/router/info.go @@ -0,0 +1,31 @@ +package router + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +var Info = new(info) + +type info struct{} + +// Init 初始化 公告 路由信息 +func (r *info) Init(public *gin.RouterGroup, private *gin.RouterGroup) { + { + group := private.Group("info").Use(middleware.OperationRecord()) + group.POST("createInfo", apiInfo.CreateInfo) // 新建公告 + group.DELETE("deleteInfo", apiInfo.DeleteInfo) // 删除公告 + group.DELETE("deleteInfoByIds", apiInfo.DeleteInfoByIds) // 批量删除公告 + group.PUT("updateInfo", apiInfo.UpdateInfo) // 更新公告 + } + { + group := private.Group("info") + group.GET("findInfo", apiInfo.FindInfo) // 根据ID获取公告 + group.GET("getInfoList", apiInfo.GetInfoList) // 获取公告列表 + } + { + group := public.Group("info") + group.GET("getInfoDataSource", apiInfo.GetInfoDataSource) // 获取公告数据源 + group.GET("getInfoPublic", apiInfo.GetInfoPublic) // 获取公告列表 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/service/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/service/enter.go new file mode 100644 index 000000000..988fbcd76 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/service/enter.go @@ -0,0 +1,5 @@ +package service + +var Service = new(service) + +type service struct{ Info info } diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/service/info.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/service/info.go new file mode 100644 index 000000000..b52155393 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/announcement/service/info.go @@ -0,0 +1,78 @@ +package service + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model/request" +) + +var Info = new(info) + +type info struct{} + +// CreateInfo 创建公告记录 +// Author [piexlmax](https://github.com/piexlmax) +func (s *info) CreateInfo(info *model.Info) (err error) { + err = global.GVA_DB.Create(info).Error + return err +} + +// DeleteInfo 删除公告记录 +// Author [piexlmax](https://github.com/piexlmax) +func (s *info) DeleteInfo(ID string) (err error) { + err = global.GVA_DB.Delete(&model.Info{}, "id = ?", ID).Error + return err +} + +// DeleteInfoByIds 批量删除公告记录 +// Author [piexlmax](https://github.com/piexlmax) +func (s *info) DeleteInfoByIds(IDs []string) (err error) { + err = global.GVA_DB.Delete(&[]model.Info{}, "id in ?", IDs).Error + return err +} + +// UpdateInfo 更新公告记录 +// Author [piexlmax](https://github.com/piexlmax) +func (s *info) UpdateInfo(info model.Info) (err error) { + err = global.GVA_DB.Model(&model.Info{}).Where("id = ?", info.ID).Updates(&info).Error + return err +} + +// GetInfo 根据ID获取公告记录 +// Author [piexlmax](https://github.com/piexlmax) +func (s *info) GetInfo(ID string) (info model.Info, err error) { + err = global.GVA_DB.Where("id = ?", ID).First(&info).Error + return +} + +// GetInfoInfoList 分页获取公告记录 +// Author [piexlmax](https://github.com/piexlmax) +func (s *info) GetInfoInfoList(info request.InfoSearch) (list []model.Info, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := global.GVA_DB.Model(&model.Info{}) + var infos []model.Info + // 如果有条件搜索 下方会自动创建搜索语句 + if info.StartCreatedAt != nil && info.EndCreatedAt != nil { + db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) + } + err = db.Count(&total).Error + if err != nil { + return + } + + if limit != 0 { + db = db.Limit(limit).Offset(offset) + } + err = db.Find(&infos).Error + return infos, total, err +} +func (s *info) GetInfoDataSource() (res map[string][]map[string]any, err error) { + res = make(map[string][]map[string]any) + + userID := make([]map[string]any, 0) + global.GVA_DB.Table("sys_users").Select("nick_name as label,id as value").Scan(&userID) + res["userID"] = userID + return +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/README.md new file mode 100644 index 000000000..63ff4400d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/README.md @@ -0,0 +1,63 @@ +[## GVA 图库功能 + +### 手动安装方法 + + 1.解压zip获得customerservice文件夹 + 2.将 customerservice/web/plugin/customerservice 放置在web/plugin下 + 3.将 customerservice/server/plugin/customerservice 放置在server/plugin下 + +#### 1. 前往GVA主程序下的initialize/router.go 在Routers 方法最末尾按照你需要的及安全模式添加本插件 + PluginInit(PublicGroup, customerservice.CreateCustomerServicePlug()) + 到gva系统,角色管理,分配角色的api权限即可,插件会自动注册api,需要手动分配。 + 注:会自动生成如下表:sys_service、sys_service_msg、sys_service_record、sys_service_reply、sys_service_script、sys_test_user +### 2. 配置说明 + +#### 2-1 后台主要功能 + + 客服管理、客服话术、客服自动回复配置等 + +#### 2-2 使用说明 + + 1、在前端vue部分路由需要手动配置: + web/src/router/index.js下新增如下配置: + { + path: '/kefu/login', + name: 'ServiceLogin', + component: () => import('@/plugin/customerservice/view/login/index.vue'), + meta:{ + client:true + } + }, + { + path: '/kefu/main', + name: 'ServiceMain', + component: () => import('@/plugin/customerservice/view/chat/index.vue'), + meta:{ + client:true + } + }, + { + path: '/kefu/test', + name: 'ServiceUserTest', + component: () => import('@/plugin/customerservice/view/chat/test.vue'), + meta:{ + client:true + } + }, + + 2、后台使用方法: + 启动gva项目,安装后在客服列表添加客服,然后可以从客服列表的进入工作台进入客服聊天页,或者打开客服登录页 + http://localhost:8080/#/kefu/login进行登录,在sys_test_user表手动新增测试聊天用户,然后打开链接 + http://localhost:8080/#/kefu/test/?uid=xx,uid的参数为你手动添加的test表自增id,就可以进行聊天测试了 + 3、此插件涉及的图片上传使用了插件管理中《图库》插件,可根据自己喜好进行替换 + 4、websocket连接的地方在插件view/chat/index.vue和test.vue下,连接地址改成自己项目地址, + 客服:websocket.value = new WebSocket(`ws://localhost:8888/service/serve_ws?token=${token.value}`) + 用户:websocket.value = new WebSocket(`ws://localhost:8888/service/ws?user_id=${uid.value}`) + 5、项目没进行过啥大的测试,仅供参考学习 + +#### 2-3 参数说明 + +### 3. 方法API + 无 + +]() \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/api.go new file mode 100644 index 000000000..e395b2aab --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/api.go @@ -0,0 +1,396 @@ +package api + +import ( + "crypto/md5" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/service" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/service/ws" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/tools" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "go.uber.org/zap" + "gorm.io/gorm" + "net/http" + "path/filepath" + "sort" + "strconv" + "time" +) + +type CustomerServiceApi struct{} + +func (cus *CustomerServiceApi) ServeWs(ctx *gin.Context) { + ws.WsServe(ctx) +} + +func (cus *CustomerServiceApi) ServeWsForKefu(ctx *gin.Context) { + ws.ServeWsForKefu(ctx) +} + +func (cus *CustomerServiceApi) HandleTransfer(c *gin.Context) { + var transferReq struct { + FromAgent string `json:"from_agent"` + ToAgent string `json:"to_agent"` + UserID string `json:"user_id"` + } + if err := c.ShouldBindJSON(&transferReq); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 更新用户与客服的映射关系 + // 例如:userAgentMap[transferReq.UserID] = transferReq.ToAgent + + c.JSON(http.StatusOK, gin.H{"status": "success"}) +} + +func (cus *CustomerServiceApi) GetKefuInfo(c *gin.Context) { + serviceIdAny, ok := c.Get("service_id") + uidStr := c.Query("uid") + var serviceId int64 + if !ok && uidStr != "" { + var recordData sysModel.SysServiceRecord + result := global.GVA_DB.Where("uid=?", uidStr).Order("update_time DESC").Limit(1).Find(&recordData) + if result.RowsAffected == 0 || result.Error != nil { + //直接查询service表 + result2 := global.GVA_DB.Model(&sysModel.SysService{}).Select("id").Where("`status`=?", 1).Order("add_time DESC").Limit(1).Scan(&serviceId) + fmt.Println(result2) + fmt.Println(serviceId) + if result2.Error != nil || result2.RowsAffected == 0 { + response.FailWithMessage("获取客服信息失败-1", c) + return + } + } else { + serviceId = recordData.ServiceId + } + + } else { + serviceId, _ = serviceIdAny.(int64) + } + var serviceData sysModel.SysService + result3 := global.GVA_DB.Select("id,uid,online,avatar,nickname,add_time,status").Where("id=?", serviceId).Where("`status`=?", 1).Order("add_time DESC").Limit(1).Find(&serviceData) + + if result3.Error != nil || result3.RowsAffected == 0 { + response.FailWithMessage("获取客服信息失败-2", c) + return + } + + response.OkWithDetailed(serviceData, "获取成功", c) +} + +func (cus *CustomerServiceApi) SendMsg(c *gin.Context) { + var msgJson ws.Message + if jsErr := c.ShouldBindJSON(&msgJson); jsErr != nil { + fmt.Println(jsErr) + response.FailWithMessage("参数有误-1", c) + return + } + fromIdStr := msgJson.Sender + toIdStr := msgJson.Receiver + content := msgJson.Content + cType := msgJson.Role + msgTypeStr := msgJson.MsgType + if content == "" || fromIdStr == "" || toIdStr == "" || msgTypeStr == "" { + response.FailWithMessage("参数有误-2", c) + return + } + toId, err_1 := strconv.ParseInt(toIdStr, 10, 64) + fromId, err_2 := strconv.ParseInt(fromIdStr, 10, 64) + msgType, err_3 := strconv.ParseInt(msgTypeStr, 10, 64) + if err_1 != nil || err_2 != nil || err_3 != nil { + response.FailWithMessage("参数有误", c) + return + } + //限流 + if !tools.LimitFreqSingle("send_message:"+c.ClientIP(), 1, 2) { + response.FailWithMessage("发送频率过快", c) + return + } + var kfInfo sysModel.SysService + var userInfo sysModel.SysTestUser + var err, err2 error + isKf := "0" + if cType == "kf" { + err = global.GVA_DB.Where("id = ?", fromId).First(&kfInfo).Error + err2 = global.GVA_DB.Where("id = ?", toId).First(&userInfo).Error + isKf = "1" + + } else if cType == "user" { + err = global.GVA_DB.Where("id = ?", toId).First(&kfInfo).Error + err2 = global.GVA_DB.Where("id = ?", fromId).First(&userInfo).Error + } + if err != nil || err2 != nil { + response.FailWithMessage("获取失败-1", c) + return + } else if errors.Is(err, gorm.ErrRecordNotFound) || errors.Is(err2, gorm.ErrRecordNotFound) { + response.FailWithMessage("获取失败-2", c) + return + } + + ser := service.ServiceGroupApp + cErr := ser.CreateMsg(kfInfo, userInfo, msgType, content, isKf) + if cErr != nil { + response.FailWithMessage("发送失败", c) + return + } + message := ws.Message{ + Sender: fromIdStr, + Receiver: toIdStr, + Content: content, + MsgType: msgTypeStr, + Role: "kf", + Timestamp: time.Now().Unix(), + } + var key string + if cType == "kf" { + //查找指定用户广播消息 + key = "user" + toIdStr + message.AvatarUrl = kfInfo.Avatar + message.Nickname = kfInfo.Nickname + } else if cType == "user" { + //查找指定客服广播消息 + key = "kf" + toIdStr + message.Role = "user" + message.AvatarUrl = userInfo.Avatar + message.Nickname = userInfo.Nickname + } + conn, ok := ws.Manager.Clients[key] + if conn != nil && ok { + sendMsg := ws.TypeMsg{ + Type: "message", + Data: message, + } + str, _ := json.Marshal(sendMsg) + conn.Send <- str + + if cType == "user" { + //客服给用户发送自动回复消息 + var autoReply sysModel.SysServiceReply + autoContent := "" + var autoMsgType int64 + aErr := global.GVA_DB.Where("is_complete = ? AND `status` = ? AND keyword = ?", 1, 1, content).First(&autoReply).Error + fmt.Println(aErr) + if aErr == nil { + fmt.Println(autoReply) + autoContent = autoReply.Content + autoMsgType = autoReply.ReplyType + } else { + aErr = global.GVA_DB.Where("is_complete = ? AND `status` = ? AND keyword LIKE ?", 0, 1, "%"+content+"%").First(&autoReply).Error + if aErr == nil { + autoContent = autoReply.Content + autoMsgType = autoReply.ReplyType + } + } + if autoContent != "" { + if autoMsgType == 2 { + autoMsgType = 3 //图片 + } + aErr = ser.CreateMsg(kfInfo, userInfo, autoMsgType, autoContent, "1") + if aErr == nil { + autoUidStr := strconv.FormatInt(userInfo.Id, 10) + message.Sender = strconv.FormatInt(kfInfo.Id, 10) + message.Receiver = autoUidStr + message.MsgType = strconv.FormatInt(autoMsgType, 10) + message.Content = autoContent + message.IsKf = 1 + message.Role = "kf" + message.AvatarUrl = kfInfo.Avatar + message.Nickname = kfInfo.Nickname + sendMsg.Data = message + autoStr, _ := json.Marshal(sendMsg) + kfConn, isOk := ws.Manager.Clients["user"+autoUidStr] + if kfConn != nil && isOk { + kfConn.Send <- autoStr + } + } + } + } + } + + response.OkWithDetailed(nil, "发送成功", c) +} + +func (cus *CustomerServiceApi) GetMsgList(c *gin.Context) { + uid := c.Query("uid") + serviceId, ok := c.Get("service_id") //jwt里解出的 + if !ok { + //前端测试用户连接请求消息列表 + serviceId = c.Query("kf_id") + } + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) + if pageSize > 20 { + pageSize = 20 + } + offset := pageSize * (page - 1) + var total int64 + var list []sysModel.SysServiceMsg + global.GVA_DB.Model(&sysModel.SysServiceMsg{}).Where("uid=?", uid).Where("service_id=?", serviceId).Count(&total) + err := global.GVA_DB.Where("uid=?", uid).Where("service_id=?", serviceId).Limit(pageSize).Offset(offset).Order("add_time desc").Find(&list).Error + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + if len(list) > 0 { + sort.Slice(list, func(i, j int) bool { + return list[i].AddTime < list[j].AddTime + }) + for k, v := range list { + decoded, _ := base64.StdEncoding.DecodeString(v.Content) + v.Content = string(decoded) + list[k] = v + } + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: page, + PageSize: pageSize, + }, "获取成功", c) +} + +func (cus *CustomerServiceApi) GetMsgUser(c *gin.Context) { + kfId, _ := c.Get("service_id") + var list []sysModel.SysServiceRecord + err := global.GVA_DB.Where("service_id=?", kfId).Find(&list).Error + if err != nil { + response.FailWithMessage("获取失败", c) + return + } + if len(list) > 0 { + //判断用户在线状况 + for k, v := range list { + userKey := "user" + strconv.FormatInt(v.Uid, 10) + isClent, ok := ws.Manager.Clients[userKey] + if ok && isClent != nil { + v.Online = 1 + } else { + v.Online = 0 + } + decoded, _ := base64.StdEncoding.DecodeString(v.Message) + v.Message = string(decoded) + //查找未读消息数 + var noCount int64 + global.GVA_DB.Model(&sysModel.SysServiceMsg{}).Where("is_view=?", 0).Where("is_kf=?", 0).Where("service_id=?", kfId).Where("uid=?", v.Uid).Count(&noCount) + v.NoRead = noCount + v.AddTimeStr = tools.FormatTimestamp(v.UpdateTime) + if v.MessageType == 3 { + v.Message = "[图片]" + } + list[k] = v + } + sort.Slice(list, func(i, j int) bool { + if list[i].Online != list[j].Online { + return list[i].Online > list[j].Online + } + return list[i].AddTime > list[j].AddTime + }) + } + response.OkWithDetailed(list, "获取成功", c) +} + +func (cus *CustomerServiceApi) SetMsgView(c *gin.Context) { + kfId, _ := c.Get("service_id") + uid := c.Query("uid") + global.GVA_DB.Model(&sysModel.SysServiceMsg{}).Where(map[string]interface{}{"is_kf": 0, "service_id": kfId, "is_view": 0, "uid": uid}).Update("is_view", 1) + response.Ok(c) +} + +func (cus *CustomerServiceApi) UploadFile(c *gin.Context) { + file, err := c.FormFile("file") + if err != nil { + response.FailWithMessage("上传失败", c) + return + } + extension := filepath.Ext(file.Filename) + newUUID := uuid.New().String() + hash := md5.Sum([]byte("gva-service" + newUUID)) + md5Pwd := hex.EncodeToString(hash[:]) + filename := md5Pwd + extension + if err := c.SaveUploadedFile(file, "./uploads/file/"+filename); err != nil { + response.FailWithMessage("上传失败-2", c) + return + + } + ser := service.ServiceGroupApp + url := ser.GetUrlHost(c) + response.OkWithDetailed(url+"api/uploads/file/"+filename, "获取成功", c) + return +} + +func (cus *CustomerServiceApi) GetTestMsgList(c *gin.Context) { + uid := c.Query("uid") + serviceId := c.Query("service_id") + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) + if pageSize > 20 { + pageSize = 20 + } + offset := pageSize * (page - 1) + var total int64 + var list []sysModel.SysServiceMsg + global.GVA_DB.Model(&sysModel.SysServiceMsg{}).Where("uid=?", uid).Where("service_id=?", serviceId).Count(&total) + err := global.GVA_DB.Where("uid=?", uid).Where("service_id=?", serviceId).Limit(pageSize).Offset(offset).Order("add_time desc").Find(&list).Error + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + return + } + if len(list) > 0 { + sort.Slice(list, func(i, j int) bool { + return list[i].AddTime < list[j].AddTime + }) + for k, v := range list { + decoded, _ := base64.StdEncoding.DecodeString(v.Content) + v.Content = string(decoded) + list[k] = v + } + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: page, + PageSize: pageSize, + }, "获取成功", c) +} + +func (cus *CustomerServiceApi) GetUserInfo(c *gin.Context) { + uidStr := c.Query("uid") + var test sysModel.SysTestUser + result := global.GVA_DB.Where("id=?", uidStr).Limit(1).Find(&test) + + if result.Error != nil || result.RowsAffected == 0 { + response.FailWithMessage("获取用户信息失败", c) + return + } + + response.OkWithDetailed(test, "获取成功", c) +} + +func (cus *CustomerServiceApi) GetServiceScript(c *gin.Context) { + rType := c.Query("type") + db := global.GVA_DB.Model(&sysModel.SysServiceScript{}) + if rType == "1" { + serviceId, ok := c.Get("service_id") + if serviceId != "" && ok { + db = db.Where("service_id=?", serviceId) + } + } else { + db = db.Where("service_id=?", 0) + } + var list []sysModel.SysServiceScript + err := db.Order("add_time desc").Limit(20).Offset(0).Find(&list).Error + if err != nil { + response.FailWithMessage("查询失败:"+err.Error(), c) + return + } + response.OkWithDetailed(list, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/enter.go new file mode 100644 index 000000000..544d361ad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/enter.go @@ -0,0 +1,8 @@ +package api + +type ApiGroup struct { + CustomerServiceApi + AdminServiceApi +} + +var ApiGroupApp = new(ApiGroup) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/service.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/service.go new file mode 100644 index 000000000..85dece9d9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/api/service.go @@ -0,0 +1,484 @@ +package api + +import ( + "crypto/md5" + "encoding/hex" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/service" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/tools" + "github.com/gin-gonic/gin" + "strconv" + "time" +) + +type AdminServiceApi struct{} + +// GetServiceList +// @Tags sysService +// @Summary 客服列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页客服列表,返回包括列表,总数,页码,每页数量" +// @Router /service/get_service_list [post] +func (ad *AdminServiceApi) GetServiceList(c *gin.Context) { + var pageInfo model.PageInfo + if err := c.ShouldBindQuery(&pageInfo); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + limit := pageInfo.Limit + offset := pageInfo.Limit * (pageInfo.Page - 1) + db := global.GVA_DB.Model(&model.SysService{}) + var list []model.SysService + var total int64 + db.Count(&total) + err := db.Omit("password").Order("add_time desc").Limit(limit).Offset(offset).Find(&list).Error + if err != nil { + response.FailWithMessage("查询失败:"+err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.Limit, + }, "获取成功", c) +} + +// SaveService +// @Tags sysService +// @Summary 添加/更新客服 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request true "" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "" +// @Router /service/save_service [post] +func (ad *AdminServiceApi) SaveService(c *gin.Context) { + var serviceData model.SysService + if err := c.ShouldBindJSON(&serviceData); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + //校验数据 + ser := service.ServiceGroupApp + if err := ser.ValidateServiceData(&serviceData); err != nil { + response.FailWithMessage("操作失败:"+err.Error(), c) + return + } + var msg string + if serviceData.Password != "" { + hash := md5.Sum([]byte("gva-service" + serviceData.Password)) + serviceData.Password = hex.EncodeToString(hash[:]) + } + + if serviceData.Id == 0 { + serviceData.AddTime = time.Now().Unix() + if err := global.GVA_DB.Create(&serviceData).Error; err != nil { + response.FailWithMessage("添加失败:"+err.Error(), c) + return + } + msg = "添加成功" + } else { + if err := global.GVA_DB.Model(&model.SysService{}).Where("id = ?", serviceData.Id).Updates(serviceData).Error; err != nil { + response.FailWithMessage("更新失败:"+err.Error(), c) + return + } + msg = "更新成功" + } + response.OkWithMessage(msg, c) +} + +// DeleteService +// @Tags sysService +// @Summary 删除客服 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/delete_service?id=xx [delete] +func (ad *AdminServiceApi) DeleteService(c *gin.Context) { + idParam := c.Query("id") + id, err := strconv.Atoi(idParam) + if err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + var ser model.SysService + if err := global.GVA_DB.First(&ser, id).Error; err != nil { + //if errors.Is(err, gorm.ErrRecordNotFound) { + // + //} + response.FailWithMessage("用户不存在:"+err.Error(), c) + return + } + // 删除用户 + if err := global.GVA_DB.Delete(&model.SysService{}, id).Error; err != nil { + response.FailWithMessage("删除失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// FindService +// @Tags sysService +// @Summary 查找客服 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/find_service?id=xx [get] +func (ad *AdminServiceApi) FindService(c *gin.Context) { + idParam := c.Query("id") + id, err := strconv.Atoi(idParam) + if err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + var ser model.SysService + if err := global.GVA_DB.First(&ser, id).Error; err != nil { + response.FailWithMessage("客服不存在:"+err.Error(), c) + return + } + ser.Password = "" + response.OkWithDetailed(ser, "success", c) +} + +// AdminServiceLogin +// @Tags sysService +// @Summary 进入工作台 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/delete_reply/:id [delete] +func (ad *AdminServiceApi) AdminServiceLogin(c *gin.Context) { + idParam := c.Query("id") + var ser model.SysService + if err := global.GVA_DB.First(&ser, idParam).Error; err != nil { + response.FailWithMessage("客服不存在:"+err.Error(), c) + return + } + data := map[string]interface{}{} + + expTime, token, err := tools.GenerateToken(ser.Id) + if err != nil { + response.FailWithMessage("登录失败:"+err.Error(), c) + return + } + data["token"] = token + data["exp_time"] = expTime + + response.OkWithDetailed(data, "success", c) +} + +// AccountServiceLogin +// @Tags sysService +// @Summary 账户密码登录 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/admin_login?id=xx [get] +func (ad *AdminServiceApi) AccountServiceLogin(c *gin.Context) { + var loginInfo model.LoginInfo + if err := c.ShouldBindJSON(&loginInfo); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + if loginInfo.Account == "" || loginInfo.Password == "" { + response.FailWithMessage("账户或密码为空", c) + return + } + var serviceInfo model.SysService + if err := global.GVA_DB.Limit(1).Where("account=?", loginInfo.Account).Find(&serviceInfo).Error; err != nil { + response.FailWithMessage("客服不存在:"+err.Error(), c) + return + } + hash := md5.Sum([]byte("gva-service" + loginInfo.Password)) + md5Pwd := hex.EncodeToString(hash[:]) + if md5Pwd != serviceInfo.Password { + response.FailWithMessage("密码不正确", c) + return + } + + data := map[string]interface{}{} + expTime, token, err := tools.GenerateToken(serviceInfo.Id) + if err != nil { + response.FailWithMessage("登录失败:"+err.Error(), c) + return + } + data["token"] = token + data["exp_time"] = expTime + response.OkWithDetailed(data, "success", c) +} + +// GetScriptList +// @Tags sysService +// @Summary 客服话术列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页客服列表,返回包括列表,总数,页码,每页数量" +// @Router /service/get_script_list [get] +func (ad *AdminServiceApi) GetScriptList(c *gin.Context) { + var pageInfo model.PageInfo + if err := c.ShouldBindQuery(&pageInfo); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + limit := pageInfo.Limit + offset := pageInfo.Limit * (pageInfo.Page - 1) + db := global.GVA_DB.Model(&model.SysServiceScript{}) + var list []model.SysServiceScript + var total int64 + db.Count(&total) + err := db.Order("sort desc,add_time desc").Limit(limit).Offset(offset).Find(&list).Error + if err != nil { + response.FailWithMessage("查询失败:"+err.Error(), c) + return + } + for k, v := range list { + t := time.Unix(v.AddTime, 0) + v.AddTimeStr = t.Format("2006-01-02 15:04:05") + list[k] = v + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.Limit, + }, "获取成功", c) +} + +// SaveScript +// @Tags sysService +// @Summary 添加/更新客服话术 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request true "" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "" +// @Router /service/save_script [post] +func (ad *AdminServiceApi) SaveScript(c *gin.Context) { + var scriptData model.SysServiceScript + if err := c.ShouldBindJSON(&scriptData); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + //校验数据 + ser := service.ServiceGroupApp + if err := ser.ValidateScriptData(&scriptData); err != nil { + response.FailWithMessage("操作失败:"+err.Error(), c) + return + } + var msg string + if scriptData.Id == 0 { + scriptData.AddTime = time.Now().Unix() + if err := global.GVA_DB.Create(&scriptData).Error; err != nil { + response.FailWithMessage("添加失败:"+err.Error(), c) + return + } + msg = "添加成功" + } else { + if err := global.GVA_DB.Model(&model.SysServiceScript{}).Where("id = ?", scriptData.Id).Updates(scriptData).Error; err != nil { + response.FailWithMessage("更新失败:"+err.Error(), c) + return + } + msg = "更新成功" + } + response.OkWithMessage(msg, c) +} + +// DeleteScript +// @Tags sysService +// @Summary 删除客服话术 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/delete_script?id=xxx [delete] +func (ad *AdminServiceApi) DeleteScript(c *gin.Context) { + idParam := c.Query("id") + id, err := strconv.Atoi(idParam) + if err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + var ser model.SysServiceScript + if err := global.GVA_DB.First(&ser, id).Error; err != nil { + response.FailWithMessage("话术不存在或已删除:"+err.Error(), c) + return + } + // 删除 + if err := global.GVA_DB.Delete(&model.SysServiceScript{}, id).Error; err != nil { + response.FailWithMessage("删除失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// FindScript +// @Tags sysService +// @Summary 查找话术 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/find_script?id=xx [get] +func (ad *AdminServiceApi) FindScript(c *gin.Context) { + idParam := c.Query("id") + id, err := strconv.Atoi(idParam) + if err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + var ser model.SysServiceScript + if err := global.GVA_DB.First(&ser, id).Error; err != nil { + response.FailWithMessage("话术不存在:"+err.Error(), c) + return + } + response.OkWithDetailed(ser, "success", c) +} + +// AutoReplyList +// @Tags sysService +// @Summary 自动回复列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "页码, 每页大小" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页客服列表,返回包括列表,总数,页码,每页数量" +// @Router /service/auto_reply_list [get] +func (ad *AdminServiceApi) AutoReplyList(c *gin.Context) { + var pageInfo model.AutoPageInfo + if err := c.ShouldBindQuery(&pageInfo); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + limit := pageInfo.Limit + offset := pageInfo.Limit * (pageInfo.Page - 1) + db := global.GVA_DB.Model(&model.SysServiceReply{}) + var list []model.SysServiceReply + var total int64 + db.Count(&total) + err := db.Order("add_time desc").Limit(limit).Offset(offset).Find(&list).Error + if err != nil { + response.FailWithMessage("查询失败:"+err.Error(), c) + return + } + for k, v := range list { + t := time.Unix(v.AddTime, 0) + v.AddTimeStr = t.Format("2006-01-02 15:04:05") + list[k] = v + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.Limit, + }, "获取成功", c) +} + +// SaveReply +// @Tags sysService +// @Summary 添加/更新自动回复 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request true "" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "" +// @Router /service/save_reply [post] +func (ad *AdminServiceApi) SaveReply(c *gin.Context) { + var replyData model.SysServiceReply + if err := c.ShouldBindJSON(&replyData); err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + //校验数据 + ser := service.ServiceGroupApp + if err := ser.ValidateReplyData(&replyData); err != nil { + response.FailWithMessage("操作失败:"+err.Error(), c) + return + } + var msg string + if replyData.Id == 0 { + replyData.AddTime = time.Now().Unix() + if err := global.GVA_DB.Create(&replyData).Error; err != nil { + response.FailWithMessage("添加失败:"+err.Error(), c) + return + } + msg = "添加成功" + } else { + if err := global.GVA_DB.Model(&model.SysServiceReply{}).Where("id = ?", replyData.Id).Updates(replyData).Error; err != nil { + response.FailWithMessage("更新失败:"+err.Error(), c) + return + } + msg = "更新成功" + } + response.OkWithMessage(msg, c) +} + +// DeleteReply +// @Tags sysService +// @Summary 删除自动回复 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/delete_reply?id=xx [delete] +func (ad *AdminServiceApi) DeleteReply(c *gin.Context) { + idParam := c.Query("id") + id, err := strconv.Atoi(idParam) + if err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + var ser model.SysServiceReply + if err := global.GVA_DB.First(&ser, id).Error; err != nil { + response.FailWithMessage("内容不存在或已删除:"+err.Error(), c) + return + } + // 删除数据 + if err := global.GVA_DB.Delete(&model.SysServiceReply{}, id).Error; err != nil { + response.FailWithMessage("删除失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// FindReply +// @Tags sysService +// @Summary 查找自动回复详情 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.id true "id" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "成功消息" +// @Router /service/find_reply?id=xxx [get] +func (ad *AdminServiceApi) FindReply(c *gin.Context) { + idParam := c.Query("id") + id, err := strconv.Atoi(idParam) + if err != nil { + response.FailWithMessage("参数错误:"+err.Error(), c) + return + } + var ser model.SysServiceReply + if err := global.GVA_DB.First(&ser, id).Error; err != nil { + response.FailWithMessage("自动回复内容不存在:"+err.Error(), c) + return + } + response.OkWithDetailed(ser, "success", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/config/config.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/config/config.go new file mode 100644 index 000000000..63acfdde1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/config/config.go @@ -0,0 +1 @@ +package config \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/global/global.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/global/global.go new file mode 100644 index 000000000..2b46851e7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/global/global.go @@ -0,0 +1 @@ +package global \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/main.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/main.go new file mode 100644 index 000000000..2ea40c6d8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/main.go @@ -0,0 +1,206 @@ +package customerservice + +import ( + gvaGlobal "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/router" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/service/ws" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" + "github.com/gin-gonic/gin" +) + +type CustomerServicePlugin struct { +} + +func CreateCustomerServicePlug() *CustomerServicePlugin { + go gvaGlobal.GVA_DB.AutoMigrate(model.SysService{}, + model.SysServiceMsg{}, + model.SysServiceRecord{}, + model.SysTestUser{}, + model.SysServiceReply{}, + model.SysServiceScript{}) // 此处可以把插件依赖的数据库结构体自动创建表 需要填写对应的结构体 + // 下方会自动注册菜单 第一个参数为菜单一级路由信息一般为定义好的组名 第二个参数为真实使用的web页面路由信息 + // 具体值请根据实际情况修改 + utils.RegisterMenus( + system.SysBaseMenu{ + Path: "service", + Name: "客服管理", + Hidden: false, + Component: "view/routerHolder.vue", + Sort: 4, + Meta: system.Meta{ + Title: "客服管理", + Icon: "service", + }, + }, + system.SysBaseMenu{ + Path: "index", + Name: "客服列表", + Hidden: false, + Component: "plugin/customerservice/view/service/index.vue", + Sort: 1, + Meta: system.Meta{ + Title: "客服列表", + Icon: "service", + }, + }, + system.SysBaseMenu{ + Path: "script/list", + Name: "客服话术", + Hidden: false, + Component: "plugin/customerservice/view/script/index.vue", + Sort: 2, + Meta: system.Meta{ + Title: "客服话术", + Icon: "document", + }, + }, + system.SysBaseMenu{ + Path: "reply/list", + Name: "自动回复", + Hidden: false, + Component: "plugin/customerservice/view/reply/index.vue", + Sort: 3, + Meta: system.Meta{ + Title: "自动回复", + Icon: "bell-filled", + }, + }, + ) + + // 下方会自动注册api 以下格式为示例格式,请按照实际情况修改 + utils.RegisterApis( + //system.SysApi{ + // Path: "/service/ws", + // Description: "用户连接接口", + // ApiGroup: "客服管理", + // Method: "GET", + //}, + //system.SysApi{ + // Path: "/service/serve_ws", + // Description: "客服连接接口", + // ApiGroup: "客服管理", + // Method: "GET", + //}, + //system.SysApi{ + // Path: "/service/send_msg", + // Description: "发送消息接口", + // ApiGroup: "客服管理", + // Method: "POST", + //}, + //system.SysApi{ + // Path: "/service/get_msg_list", + // Description: "消息列表", + // ApiGroup: "客服管理", + // Method: "GET", + //}, + //system.SysApi{ + // Path: "/service/get_msg_user", + // Description: "客服聊天用户列表", + // ApiGroup: "客服管理", + // Method: "GET", + //}, + //system.SysApi{ + // Path: "/service/get_kf_info", + // Description: "当前客服详情", + // ApiGroup: "客服管理", + // Method: "GET", + //}, + //system.SysApi{ + // Path: "/service/set_msg_view", + // Description: "设置已读", + // ApiGroup: "客服管理", + // Method: "GET", + //}, + system.SysApi{ + Path: "/service/get_service_list", + Description: "后台客服列表", + ApiGroup: "客服管理", + Method: "GET", + }, + system.SysApi{ + Path: "/service/save_service", + Description: "后台客服新增/更新", + ApiGroup: "客服管理", + Method: "POST", + }, + system.SysApi{ + Path: "/service/delete_service", + Description: "删除客服", + ApiGroup: "客服管理", + Method: "DELETE", + }, + system.SysApi{ + Path: "/service/find_service", + Description: "客服详情", + ApiGroup: "客服管理", + Method: "GET", + }, + system.SysApi{ + Path: "/service/get_script_list", + Description: "客服话术列表", + ApiGroup: "客服管理", + Method: "GET", + }, + system.SysApi{ + Path: "/service/save_script", + Description: "客服话术新增/更新", + ApiGroup: "客服管理", + Method: "POST", + }, + system.SysApi{ + Path: "/service/delete_script", + Description: "删除客服话术", + ApiGroup: "客服管理", + Method: "DELETE", + }, + system.SysApi{ + Path: "/service/find_script", + Description: "客服话术详情", + ApiGroup: "客服管理", + Method: "GET", + }, + system.SysApi{ + Path: "/service/auto_reply_list", + Description: "自动回复列表", + ApiGroup: "客服管理", + Method: "GET", + }, + system.SysApi{ + Path: "/service/save_reply", + Description: "自动回复新增/更新", + ApiGroup: "客服管理", + Method: "POST", + }, + system.SysApi{ + Path: "/service/delete_reply", + Description: "删除自动回复", + ApiGroup: "客服管理", + Method: "DELETE", + }, + system.SysApi{ + Path: "/service/find_reply", + Description: "自动回复详情", + ApiGroup: "客服管理", + Method: "GET", + }, + system.SysApi{ + Path: "/service/admin_login", + Description: "进入客服工作台", + ApiGroup: "客服管理", + Method: "GET", + }, + ) + go ws.Manager.Start() + go ws.Manager.CheckClientActivity() + return &CustomerServicePlugin{} +} + +func (*CustomerServicePlugin) Register(group *gin.RouterGroup) { + router.RouterGroupApp.InitCustomerServiceRouter(group) +} + +func (*CustomerServicePlugin) RouterPath() string { + return "" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/middleware/jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/middleware/jwt.go new file mode 100644 index 000000000..bae4a1a68 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/middleware/jwt.go @@ -0,0 +1,45 @@ +package middleware + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/tools" + "github.com/gin-gonic/gin" + "strings" +) + +func JWTAuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 从请求头获取 token + authHeader := c.GetHeader("chat-token") + userHeader := c.GetHeader("user-token") + if userHeader != "" { + c.Next() // 如果是前端发送的测试请求直接放行 + return + } + if authHeader == "" { + response.FailWithMessage("参数错误:"+"Authorization header is missing", c) + c.Abort() + return + } + + // 按照格式 "Bearer " 提取 token + tokenString := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer ")) + if tokenString == "" { + response.FailWithMessage("参数错误:"+"Token is missing", c) + c.Abort() + return + } + // 验证 token + claims, err := tools.ValidateToken(tokenString) + if err != nil { + response.FailWithMessage("Invalid token:"+err.Error(), c) + c.Abort() + return + } + + // 将用户信息存储在上下文中,便于后续处理 + c.Set("service_id", claims.ServiceId) + //c.Request.URL.Query().Add("service_id", strconv.FormatInt(claims.ServiceId, 10)) + c.Next() // 继续处理请求 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/model.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/model.go new file mode 100644 index 000000000..8c229488a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/model.go @@ -0,0 +1,25 @@ +package model + +type PageInfo struct { + Page int `json:"page" form:"page"` + Limit int `json:"limit" form:"limit"` + Keyword string `json:"keyword" form:"keyword"` +} + +type MsgPageInfo struct { + Page int `json:"page" form:"page"` + Limit int `json:"limit" form:"limit"` + FromId int `json:"from_id" form:"from_id"` +} + +type AutoPageInfo struct { + Page int `json:"page" form:"page"` + Limit int `json:"limit" form:"limit"` + Keyword string `json:"keyword" form:"keyword"` + ReplyType int `json:"reply_type" form:"reply_type"` +} + +type LoginInfo struct { + Account string `json:"account" form:"account"` + Password string `json:"password" form:"password"` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysService.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysService.go new file mode 100644 index 000000000..ea550d8d6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysService.go @@ -0,0 +1,18 @@ +package model + +type SysService struct { + Id int64 `json:"id" form:"id" gorm:"primarykey"` + MerchantId uint `json:"merchant_id" form:"merchant_id" gorm:"default:0;type:int;column:merchant_id;comment:商户id;"` + Uid uint `json:"uid" form:"uid" gorm:"default:0;type:int;column:uid;comment:用户id;"` + Online uint `json:"online" form:"online" gorm:"default:0;type:tinyint;column:online;comment:客服是否在线;"` + Account string `json:"account" form:"account" gorm:"default:'';type:varchar(255);column:account;comment:账户;"` + Password string `json:"password" form:"password" gorm:"default:'';type:varchar(255);column:password;comment:密码;"` + Avatar string `json:"avatar" form:"avatar" gorm:"default:'';type:varchar(255);column:avatar;comment:头像;"` + Nickname string `json:"nickname" form:"nickname" gorm:"default:'';type:varchar(255);column:nickname;comment:客服名称;"` + AddTime int64 `json:"add_time" form:"add_time" gorm:"default:0;type:int;column:add_time;comment:添加时间;"` + Status *uint `json:"status" form:"status" gorm:"default:0;type:tinyint(1);column:status;comment:是否显示;"` +} + +func (SysService) TableName() string { + return "sys_service" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceMsg.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceMsg.go new file mode 100644 index 000000000..3dbb2d2fc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceMsg.go @@ -0,0 +1,18 @@ +package model + +type SysServiceMsg struct { + Id uint `gorm:"primarykey" json:"id"` // 主键ID + MerchantId uint `json:"merchant_id" form:"merchant_id" gorm:"default:0;type:int;column:merchant_id;comment:商户id;"` + Content string `json:"content" form:"content" gorm:"default:'';type:text;column:content;comment:消息内容;"` + ServiceId int64 `json:"service_id" form:"service_id" gorm:"default:0;type:int;column:service_id;comment:客服id;"` + Uid int64 `json:"uid" form:"uid" gorm:"default:0;type:int;column:uid;comment:用户id;"` + IsTourist uint `json:"is_tourist" form:"is_tourist" gorm:"default:0;type:tinyint;column:is_tourist;comment:是否游客;"` + IsView uint `json:"is_view" form:"is_view" gorm:"default:0;type:tinyint;column:is_view;comment:是否已读;"` + AddTime int `json:"add_time" form:"add_time" gorm:"default:0;type:int;column:add_time;comment:添加时间;"` + MsgType int64 `json:"msg_type" form:"msg_type" gorm:"default:1;type:tinyint;column:msg_type;comment:消息类型 1=文字 2=表情 3=图片 4=语音 5=视频 6=商品;"` + IsKf int64 `json:"is_kf" form:"is_kf" gorm:"default:0;type:tinyint;column:is_kf;comment:是否客服消息;"` +} + +func (SysServiceMsg) TableName() string { + return "sys_service_msg" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceRecord.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceRecord.go new file mode 100644 index 000000000..8c4ecba57 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceRecord.go @@ -0,0 +1,21 @@ +package model + +type SysServiceRecord struct { + Id uint `json:"id" form:"id" gorm:"primarykey"` + ServiceId int64 `json:"service_id" form:"service_id" gorm:"default:0;type:int;column:service_id;comment:客服id;"` + Uid int64 `json:"uid" form:"uid" gorm:"default:0;type:int;column:uid;comment:用户id;"` + Avatar string `json:"avatar" form:"avatar" gorm:"default:'';type:varchar(255);column:avatar;comment:用户头像;"` + Nickname string `json:"nickname" form:"nickname" gorm:"default:'';type:varchar(255);column:nickname;comment:用户昵称;"` + Online uint `json:"online" form:"online" gorm:"default:0;type:tinyint;column:online;comment:是否在线;"` + IsTourist uint `json:"is_tourist" form:"is_tourist" gorm:"default:0;type:tinyint;column:is_tourist;comment:是否游客0:否;1:是;"` + Message string `json:"message" form:"message" gorm:"default:'';type:text;column:message;comment:最新一条消息;"` + AddTime int64 `json:"add_time" form:"add_time" gorm:"default:0;type:int;column:add_time;comment:添加时间;"` + UpdateTime int64 `json:"update_time" form:"update_time" gorm:"default:0;type:int;column:update_time;comment:更新时间;"` + MessageType int64 `json:"message_type" form:"message_type" gorm:"default:0;type:tinyint(1);column:message_type;comment:消息类型:1=文字 2=表情 3=图片 4=语音 5=视频 6=商品;"` + NoRead int64 `json:"no_read" gorm:"-"` + AddTimeStr string `json:"add_time_str" gorm:"-"` +} + +func (SysServiceRecord) TableName() string { + return "sys_service_record" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceReply.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceReply.go new file mode 100644 index 000000000..ea92f129b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceReply.go @@ -0,0 +1,16 @@ +package model + +type SysServiceReply struct { + Id int64 `json:"id" form:"id" gorm:"primarykey"` + ReplyType int64 `json:"reply_type" form:"reply_type" gorm:"default:1;type:int;column:reply_type;comment:回复类型1文本,2图片;"` + IsComplete int64 `json:"is_complete" form:"is_complete" gorm:"default:0;type:int;column:is_complete;comment:是否完全匹配0否1是;"` + Keyword string `json:"keyword" form:"keyword" gorm:"default:'';type:varchar(255);column:keyword;comment:关键字;"` + Content string `json:"content" form:"content" gorm:"default:'';type:text;column:content;comment:回复内容;"` + AddTime int64 `json:"add_time" form:"add_time" gorm:"default:0;type:int;column:add_time;comment:添加时间;"` + AddTimeStr string `json:"add_time_str" form:"add_time_str" gorm:"-"` + Status int64 `json:"status" form:"sort" gorm:"default:0;type:tinyint(1);column:status;comment:是否显示;"` +} + +func (SysServiceReply) TableName() string { + return "sys_service_reply" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceScript.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceScript.go new file mode 100644 index 000000000..7605d0d50 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysServiceScript.go @@ -0,0 +1,15 @@ +package model + +type SysServiceScript struct { + Id int64 `json:"id" form:"id" gorm:"primarykey"` + ServiceId int64 `json:"service_id" form:"service_id" gorm:"default:0;type:int;column:service_id;comment:客服id为0说明是公共话术;"` + Title string `json:"title" form:"title" gorm:"default:'';type:varchar(255);column:title;comment:话术标题;"` + Content string `json:"content" form:"content" gorm:"default:'';type:text;column:content;comment:话术内容;"` + AddTime int64 `json:"add_time" form:"add_time" gorm:"default:0;type:int;column:add_time;comment:添加时间;"` + AddTimeStr string `json:"add_time_str" form:"add_time_str" gorm:"-"` + Sort int64 `json:"sort" form:"sort" gorm:"default:0;type:int;column:sort;comment:排序;"` +} + +func (SysServiceScript) TableName() string { + return "sys_service_script" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysTestUser.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysTestUser.go new file mode 100644 index 000000000..1864e85a9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/model/sysTestUser.go @@ -0,0 +1,11 @@ +package model + +type SysTestUser struct { + Id int64 `json:"id" form:"id" gorm:"primarykey"` + Avatar string `json:"avatar" form:"avatar" gorm:"default:'';type:varchar(255);column:avatar;comment:头像;"` + Nickname string `json:"nickname" form:"nickname" gorm:"default:'';type:varchar(255);column:nickname;comment:昵称;"` +} + +func (SysTestUser) TableName() string { + return "sys_test_user" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/router/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/router/enter.go new file mode 100644 index 000000000..1ce0253ca --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/router/enter.go @@ -0,0 +1,7 @@ +package router + +type RouterGroup struct { + CustomerServiceRouter +} + +var RouterGroupApp = new(RouterGroup) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/router/router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/router/router.go new file mode 100644 index 000000000..62590501e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/router/router.go @@ -0,0 +1,47 @@ +package router + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/api" + serMiddleware "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/middleware" + "github.com/gin-gonic/gin" +) + +type CustomerServiceRouter struct { +} + +func (s *CustomerServiceRouter) InitCustomerServiceRouter(Router *gin.RouterGroup) { + wsRouter := Router.Group("") + plugRouter := Router.Group("").Use(serMiddleware.JWTAuthMiddleware()) + privateRouter := Router.Group("").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) + plugAdminApi := api.ApiGroupApp.AdminServiceApi + { + privateRouter.GET("/service/get_service_list", plugAdminApi.GetServiceList) + privateRouter.POST("/service/save_service", plugAdminApi.SaveService) + privateRouter.DELETE("/service/delete_service", plugAdminApi.DeleteService) + privateRouter.GET("/service/find_service", plugAdminApi.FindService) + privateRouter.GET("/service/admin_login", plugAdminApi.AdminServiceLogin) + privateRouter.GET("/service/get_script_list", plugAdminApi.GetScriptList) + privateRouter.POST("/service/save_script", plugAdminApi.SaveScript) + privateRouter.DELETE("/service/delete_script", plugAdminApi.DeleteScript) + privateRouter.GET("/service/find_script", plugAdminApi.FindScript) + privateRouter.GET("/service/auto_reply_list", plugAdminApi.AutoReplyList) + privateRouter.POST("/service/save_reply", plugAdminApi.SaveReply) + privateRouter.DELETE("/service/delete_reply", plugAdminApi.DeleteReply) + privateRouter.GET("/service/find_reply", plugAdminApi.FindReply) + } + plugApi := api.ApiGroupApp.CustomerServiceApi + { + plugRouter.POST("/service/send_msg", plugApi.SendMsg) + plugRouter.GET("/service/get_msg_list", plugApi.GetMsgList) + plugRouter.GET("/service/get_msg_user", plugApi.GetMsgUser) + plugRouter.GET("/service/get_kf_info", plugApi.GetKefuInfo) + plugRouter.GET("/service/set_msg_view", plugApi.SetMsgView) + plugRouter.POST("/service/upload_file", plugApi.UploadFile) + plugRouter.GET("/service/get_user_info", plugApi.GetUserInfo) + plugRouter.GET("/service/get_service_script", plugApi.GetServiceScript) + } + wsRouter.GET("/service/serve_ws", plugApi.ServeWsForKefu) + wsRouter.GET("/service/ws", plugApi.ServeWs) + wsRouter.POST("/service/account_login", plugAdminApi.AccountServiceLogin) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/enter.go new file mode 100644 index 000000000..d2feab111 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/enter.go @@ -0,0 +1,7 @@ +package service + +type ServiceGroup struct { + CustomerServiceService +} + +var ServiceGroupApp = new(ServiceGroup) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/service.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/service.go new file mode 100644 index 000000000..986d2c92d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/service.go @@ -0,0 +1,129 @@ +package service + +import ( + "encoding/base64" + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/model" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "time" +) + +type CustomerServiceService struct{} + +func (e *CustomerServiceService) PlugService() (err error) { + // 写你的业务逻辑 + return nil +} + +func (e *CustomerServiceService) ValidateServiceData(sys *model.SysService) error { + if sys.Uid == 0 { + return errors.New("客服关联的用户id不能为空") + } else { + db := global.GVA_DB.Model(&model.SysService{}) + if sys.Id > 0 { + db = db.Where("uid=?", sys.Uid).Where("id<>?", sys.Id) + } else { + db = db.Where("uid=?", sys.Uid) + } + var dCount int64 + db.Count(&dCount) + if dCount > 0 { + return errors.New("用户id已关联其他客服,请重新输入") + } + } + db := global.GVA_DB.Model(&model.SysService{}) + if sys.Id == 0 { + if sys.Password == "" { + return errors.New("客服密码必须填写") + } + db = db.Where("account=?", sys.Account) + } else { + db = db.Where("account=?", sys.Account).Where("id<>?", sys.Id) + var dCount int64 + db.Count(&dCount) + if dCount > 0 { + return errors.New("账户已存在,请重新输入") + } + } + if sys.Account == "" { + return errors.New("客服账户必须填写") + } + if sys.Nickname == "" { + return errors.New("客服名称必须填写") + } + if sys.Avatar == "" { + return errors.New("客服头像必须选择") + } + + return nil +} + +func (e *CustomerServiceService) ValidateScriptData(sys *model.SysServiceScript) error { + if sys.Title == "" { + return errors.New("话术标题必须填写") + } + if sys.Content == "" { + return errors.New("话术内容必须填写") + } + return nil +} + +func (e *CustomerServiceService) ValidateReplyData(sys *model.SysServiceReply) error { + if sys.Keyword == "" { + return errors.New("关键字必须填写") + } + if sys.Content == "" { + return errors.New("回复内容必须填写") + } + return nil +} + +func (e *CustomerServiceService) GetUrlHost(c *gin.Context) string { + host := c.Request.Host + scheme := "http" + if c.Request.TLS != nil { + scheme = "https" + } + referer := c.Request.Referer() + if referer != "" { + return referer + } + return scheme + "://" + host + "/" +} + +func (e *CustomerServiceService) CreateMsg(kfInfo model.SysService, userInfo model.SysTestUser, msgType int64, content string, isKf string) (err error) { + msgRecord := &model.SysServiceRecord{ + ServiceId: kfInfo.Id, + Uid: userInfo.Id, + Message: base64.StdEncoding.EncodeToString([]byte(content)), + MessageType: msgType, + UpdateTime: time.Now().Unix(), + Avatar: userInfo.Avatar, + Nickname: userInfo.Nickname, + Online: 1, + } + var record model.SysServiceRecord + + eErr := global.GVA_DB.Where("service_id = ?", kfInfo.Id).Where("uid = ?", userInfo.Id).First(&record).Error + if errors.Is(eErr, gorm.ErrRecordNotFound) { + msgRecord.AddTime = time.Now().Unix() + global.GVA_DB.Create(msgRecord) + } else { + global.GVA_DB.Model(&model.SysServiceRecord{}).Where("id = ?", record.Id).Updates(msgRecord) + } + + //插入消息记录 + msg := map[string]interface{}{ + "service_id": kfInfo.Id, + "uid": userInfo.Id, + "content": base64.StdEncoding.EncodeToString([]byte(content)), + "msg_type": msgType, + "is_view": 0, + "add_time": time.Now().Unix(), + "is_kf": isKf, + } + err = global.GVA_DB.Model(&model.SysServiceMsg{}).Create(msg).Error + return err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/ws/ws.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/ws/ws.go new file mode 100644 index 000000000..8bae71a40 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/service/ws/ws.go @@ -0,0 +1,259 @@ +package ws + +import ( + "encoding/json" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/customerservice/tools" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "net/http" + "strconv" + "time" +) + +type Message struct { + Sender string `json:"sender"` + Receiver string `json:"receiver"` + Content string `json:"content"` + MsgType string `json:"msg_type"` //对应msg表的msg_type + Role string `json:"role"` + Timestamp int64 `json:"timestamp"` + Nickname string `json:"nickname"` + AvatarUrl string `json:"avatar_url"` + IsKf int64 `json:"is_kf"` +} + +type TypeMsg struct { + Type string `json:"type"` + Data interface{} `json:"data,omitempty"` +} + +type Client struct { + UserID string + Role string + Socket *websocket.Conn + Send chan []byte + LastPingTime time.Time +} + +type ClientManager struct { + Clients map[string]*Client + Broadcast chan TypeMsg + Register chan *Client + Unregister chan *Client +} + +var Manager = ClientManager{ + Clients: make(map[string]*Client), + Broadcast: make(chan TypeMsg), + Register: make(chan *Client), + Unregister: make(chan *Client), +} + +// 定时检查连接的活动状态 +func (manager *ClientManager) CheckClientActivity() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + <-ticker.C + now := time.Now() + + for ck, client := range manager.Clients { + // 如果超过一定时间没有收到ping,则断开连接 + fmt.Println(ck) + fmt.Println(now.Sub(client.LastPingTime)) + if now.Sub(client.LastPingTime) > 120*time.Second { + client.Socket.Close() + delete(manager.Clients, ck) + //设置离线 + if client.Role == "user" { + setUserOnline("offline", client.UserID) + } + } + } + } +} + +func (manager *ClientManager) Start() { + for { + select { + case conn := <-manager.Register: + key := conn.Role + conn.UserID + if existingConn, ok := manager.Clients[key]; ok { + existingConn.Socket.Close() + delete(manager.Clients, key) + } + fmt.Println(key) + manager.Clients[key] = conn + case conn := <-manager.Unregister: + key := conn.Role + conn.UserID + if existingConn, ok := manager.Clients[key]; ok && existingConn == conn { + delete(manager.Clients, key) + } + case message := <-manager.Broadcast: + data := message.Data.(map[string]interface{}) + receiver := data["receiver"].(string) + receiverKey := "user" + receiver + if data["role"].(string) == "user" { + receiverKey = "kf" + receiver + } + if client, ok := manager.Clients[receiverKey]; ok { + str, _ := json.Marshal(message) + client.Send <- str + } else { + fmt.Println(receiverKey + "链接不存在") + } + } + } +} + +func (c *Client) Read() { + defer func() { + Manager.Unregister <- c + c.Socket.Close() + }() + c.Socket.SetReadLimit(512) + + for { + _, message, err := c.Socket.ReadMessage() + if err != nil { + break + } + var msg TypeMsg + if err := json.Unmarshal(message, &msg); err != nil { + continue + } + switch msg.Type { + case "ping": + // 更新最后一次收到ping消息的时间 + c.LastPingTime = time.Now() + + // 回复pong消息 + pongMsg := TypeMsg{ + Type: "pong", + Data: time.Now().Unix(), + } + pongStr, _ := json.Marshal(pongMsg) + c.Send <- pongStr + + case "message": + //发送消息走的后台接口去触发广播,改成前端发送消息走这里 + Manager.Broadcast <- msg + } + } +} + +func (c *Client) Write() { + defer func() { + c.Socket.Close() + }() + for { + select { + case message, ok := <-c.Send: + c.Socket.SetWriteDeadline(time.Now().Add(10 * time.Second)) + if !ok { + c.Socket.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + if err := c.Socket.WriteMessage(websocket.TextMessage, message); err != nil { + return + } + } + } +} + +var Upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +func WsServe(ctx *gin.Context) { + //token := ctx.Query("token") + userID := ctx.Query("user_id") + + conn, err := Upgrader.Upgrade(ctx.Writer, ctx.Request, nil) + if err != nil { + http.NotFound(ctx.Writer, ctx.Request) + return + } + client := &Client{ + UserID: userID, + Role: "user", + Socket: conn, + Send: make(chan []byte), + LastPingTime: time.Now(), + } + + Manager.Register <- client + setUserOnline("online", userID) + go client.Read() + go client.Write() +} + +func ServeWsForKefu(ctx *gin.Context) { + token := ctx.Query("token") + claims, err := tools.ValidateToken(token) + if err != nil { + response.FailWithMessage("token已失效", ctx) + return + } + kfId := claims.ServiceId + db := global.GVA_DB.Model(&sysModel.SysService{}) + var info sysModel.SysService + err = db.Find(&info).Error + if err != nil { + response.FailWithMessage("客服不存在", ctx) + return + } + conn, err2 := Upgrader.Upgrade(ctx.Writer, ctx.Request, nil) + if err2 != nil { + http.NotFound(ctx.Writer, ctx.Request) + return + } + client := &Client{ + UserID: fmt.Sprintf("%v", kfId), + Role: "kf", + Socket: conn, + Send: make(chan []byte), + LastPingTime: time.Now(), + } + Manager.Register <- client + + go client.Read() + go client.Write() +} + +func setUserOnline(cType string, Id string) { + //给用户在record表里的客服广播此用户离线 + var list []sysModel.SysServiceRecord + err := global.GVA_DB.Where("uid=?", Id).Find(&list).Error + if err == nil && len(list) > 0 { + for _, rec := range list { + strSerId := strconv.FormatInt(rec.ServiceId, 10) + roleKey := "kf" + strSerId + fmt.Println(roleKey) + serviceClient, ok := Manager.Clients[roleKey] + if serviceClient != nil && ok { + dataMsg := Message{ + MsgType: "1", + Sender: Id, + Receiver: strSerId, + Role: "user", + } + sendMsg := TypeMsg{ + Type: cType, + Data: dataMsg, + } + str, _ := json.Marshal(sendMsg) + serviceClient.Send <- str + } + } + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/jwt.go new file mode 100644 index 000000000..5e2a76146 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/jwt.go @@ -0,0 +1,54 @@ +package tools + +import ( + "errors" + "github.com/golang-jwt/jwt/v4" + "time" +) + +var jwtKey = []byte("your-256-bit-secret") + +// CustomClaims 结构体可以根据需要添加自定义的声明 +type CustomClaims struct { + ServiceId int64 `json:"service_id"` + jwt.RegisteredClaims +} + +func GenerateToken(serviceId int64) (int64, string, error) { + // 设置JWT的声明 + claims := CustomClaims{ + ServiceId: serviceId, + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"GVA"}, // 受众 + NotBefore: jwt.NewNumericDate(time.Now().Add(-1000)), // 签名生效时间 + Issuer: "gva", + ExpiresAt: jwt.NewNumericDate(time.Now().Add(72 * time.Hour)), // token 72小时后过期 + }, + } + // 生成JWT token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenStr, err := token.SignedString(jwtKey) + return time.Now().Add(72 * time.Hour).Unix(), tokenStr, err +} + +func ValidateToken(tokenString string) (*CustomClaims, error) { + claims := &CustomClaims{} + + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + // 验证签名方法 + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, errors.New("unexpected signing method") + } + return jwtKey, nil + }) + + if err != nil { + return nil, err + } + + if !token.Valid { + return nil, errors.New("invalid token") + } + + return claims, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/limits.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/limits.go new file mode 100644 index 000000000..853010486 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/limits.go @@ -0,0 +1,77 @@ +package tools + +import ( + "log" + "sync" + "time" +) + +type LimitQueeMap struct { + sync.RWMutex + LimitQueue map[string][]int64 +} + +func (l *LimitQueeMap) readMap(key string) ([]int64, bool) { + l.RLock() + value, ok := l.LimitQueue[key] + l.RUnlock() + return value, ok +} + +func (l *LimitQueeMap) writeMap(key string, value []int64) { + l.Lock() + l.LimitQueue[key] = value + l.Unlock() +} + +var LimitQueue = &LimitQueeMap{ + LimitQueue: make(map[string][]int64), +} +var ok bool + +func NewLimitQueue() { + cleanLimitQueue() +} +func cleanLimitQueue() { + go func() { + for { + log.Println("cleanLimitQueue start...") + LimitQueue.LimitQueue = nil + now := time.Now() + // 计算下一个零点 + next := now.Add(time.Hour * 24) + next = time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location()) + t := time.NewTimer(next.Sub(now)) + <-t.C + } + }() +} + +//单机时间滑动窗口限流法 +func LimitFreqSingle(queueName string, count uint, timeWindow int64) bool { + currTime := time.Now().Unix() + if LimitQueue.LimitQueue == nil { + LimitQueue.LimitQueue = make(map[string][]int64) + } + if _, ok = LimitQueue.readMap(queueName); !ok { + LimitQueue.writeMap(queueName, make([]int64, 0)) + return true + } + q, _ := LimitQueue.readMap(queueName) + //队列未满 + if uint(len(q)) < count { + LimitQueue.writeMap(queueName, append(q, currTime)) + return true + } + //队列满了,取出最早访问的时间 + earlyTime := q[0] + //说明最早期的时间还在时间窗口内,还没过期,所以不允许通过 + if currTime-earlyTime <= timeWindow { + return false + } else { + //说明最早期的访问应该过期了,去掉最早期的 + q = q[1:] + LimitQueue.writeMap(queueName, append(q, currTime)) + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/timeformat.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/timeformat.go new file mode 100644 index 000000000..cd9b9407a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/customerservice/tools/timeformat.go @@ -0,0 +1,20 @@ +package tools + +import "time" + +func FormatTimestamp(timestamp int64) string { + t := time.Unix(timestamp, 0) + now := time.Now() + + // 格式化时间 + if t.Year() == now.Year() && t.YearDay() == now.YearDay() { + // 当天,返回 24 小时制的时和分 + return t.Format("15:04") + } else if t.Year() == now.Year() && t.YearDay() == now.YearDay()-1 { + // 昨天,返回 "昨天" + return "昨天" + } else { + // 其他时间,返回月和日 + return t.Format("01-02") + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/README.MD b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/README.MD new file mode 100644 index 000000000..17202838d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/README.MD @@ -0,0 +1,75 @@ +## GVA 邮件发送功能插件 +#### 开发者:GIN-VUE-ADMIN 官方 + +### 使用步骤 + +#### 1. 前往GVA主程序下的initialize/router.go 在Routers 方法最末尾按照你需要的及安全模式添加本插件 + 例: + 本插件可以采用gva的配置文件 也可以直接写死内容作为配置 建议为gva添加配置文件结构 然后将配置传入 + PluginInit(PrivateGroup, email.CreateEmailPlug( + global.GVA_CONFIG.Email.To, + global.GVA_CONFIG.Email.From, + global.GVA_CONFIG.Email.Host, + global.GVA_CONFIG.Email.Secret, + global.GVA_CONFIG.Email.Nickname, + global.GVA_CONFIG.Email.Port, + global.GVA_CONFIG.Email.IsSSL, + )) + + 同样也可以再传入时写死 + + PluginInit(PrivateGroup, email.CreateEmailPlug( + "a@qq.com", + "b@qq.com", + "smtp.qq.com", + "global.GVA_CONFIG.Email.Secret", + "登录密钥", + 465, + true, + )) + +### 2. 配置说明 + +#### 2-1 全局配置结构体说明 + //其中 Form 和 Secret 通常来说就是用户名和密码 + + type Email struct { + To string // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 此处配置主要用于发送错误监控邮件 + From string // 发件人 你自己要发邮件的邮箱 + Host string // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 + Secret string // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 + Nickname string // 昵称 发件人昵称 自定义即可 可以不填 + Port int // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 + IsSSL bool // 是否SSL 是否开启SSL + } +#### 2-2 入参结构说明 + //其中 Form 和 Secret 通常来说就是用户名和密码 + + type Email struct { + To string `json:"to"` // 邮件发送给谁 + Subject string `json:"subject"` // 邮件标题 + Body string `json:"body"` // 邮件内容 + } + + +### 3. 方法API + + utils.EmailTest(邮件标题,邮件主体) 发送测试邮件 + 例:utils.EmailTest("测试邮件","测试邮件") + utils.ErrorToEmail(邮件标题,邮件主体) 错误监控 + 例:utils.ErrorToEmail("测试邮件","测试邮件") + utils.Email(目标邮箱多个的话用逗号分隔,邮件标题,邮件主体) 发送测试邮件 + 例:utils.Email(”a.qq.com,b.qq.com“,"测试邮件","测试邮件") + +### 4. 可直接调用的接口 + + 测试接口: /email/emailTest [post] 已配置swagger + + 发送邮件接口接口: /email/emailSend [post] 已配置swagger + 入参: + type Email struct { + To string `json:"to"` // 邮件发送给谁 + Subject string `json:"subject"` // 邮件标题 + Body string `json:"body"` // 邮件内容 + } + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/api/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/api/enter.go new file mode 100644 index 000000000..353404d2c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/api/enter.go @@ -0,0 +1,7 @@ +package api + +type ApiGroup struct { + EmailApi +} + +var ApiGroupApp = new(ApiGroup) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/api/sys_email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/api/sys_email.go new file mode 100644 index 000000000..fdc76ab64 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/api/sys_email.go @@ -0,0 +1,53 @@ +package api + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + email_response "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/model/response" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/service" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type EmailApi struct{} + +// EmailTest +// @Tags System +// @Summary 发送测试邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" +// @Router /email/emailTest [post] +func (s *EmailApi) EmailTest(c *gin.Context) { + err := service.ServiceGroupApp.EmailTest() + if err != nil { + global.GVA_LOG.Error("发送失败!", zap.Error(err)) + response.FailWithMessage("发送失败", c) + return + } + response.OkWithMessage("发送成功", c) +} + +// SendEmail +// @Tags System +// @Summary 发送邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body email_response.Email true "发送邮件必须的参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" +// @Router /email/sendEmail [post] +func (s *EmailApi) SendEmail(c *gin.Context) { + var email email_response.Email + err := c.ShouldBindJSON(&email) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + err = service.ServiceGroupApp.SendEmail(email.To, email.Subject, email.Body) + if err != nil { + global.GVA_LOG.Error("发送失败!", zap.Error(err)) + response.FailWithMessage("发送失败", c) + return + } + response.OkWithMessage("发送成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/config/email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/config/email.go new file mode 100644 index 000000000..c535348c0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/config/email.go @@ -0,0 +1,11 @@ +package config + +type Email struct { + To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 + From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱 + Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 + Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 + Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱 + Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 + IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 是否开启SSL +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/global/gloabl.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/global/gloabl.go new file mode 100644 index 000000000..13082d0db --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/global/gloabl.go @@ -0,0 +1,5 @@ +package global + +import "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/config" + +var GlobalConfig = new(config.Email) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/main.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/main.go new file mode 100644 index 000000000..cfc8c46b1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/main.go @@ -0,0 +1,28 @@ +package email + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/router" + "github.com/gin-gonic/gin" +) + +type emailPlugin struct{} + +func CreateEmailPlug(To, From, Host, Secret, Nickname string, Port int, IsSSL bool) *emailPlugin { + global.GlobalConfig.To = To + global.GlobalConfig.From = From + global.GlobalConfig.Host = Host + global.GlobalConfig.Secret = Secret + global.GlobalConfig.Nickname = Nickname + global.GlobalConfig.Port = Port + global.GlobalConfig.IsSSL = IsSSL + return &emailPlugin{} +} + +func (*emailPlugin) Register(group *gin.RouterGroup) { + router.RouterGroupApp.InitEmailRouter(group) +} + +func (*emailPlugin) RouterPath() string { + return "email" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/model/response/email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/model/response/email.go new file mode 100644 index 000000000..ed2547507 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/model/response/email.go @@ -0,0 +1,7 @@ +package response + +type Email struct { + To string `json:"to"` // 邮件发送给谁 + Subject string `json:"subject"` // 邮件标题 + Body string `json:"body"` // 邮件内容 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/router/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/router/enter.go new file mode 100644 index 000000000..e081a54c3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/router/enter.go @@ -0,0 +1,7 @@ +package router + +type RouterGroup struct { + EmailRouter +} + +var RouterGroupApp = new(RouterGroup) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/router/sys_email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/router/sys_email.go new file mode 100644 index 000000000..1f9f07f0e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/router/sys_email.go @@ -0,0 +1,19 @@ +package router + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/api" + "github.com/gin-gonic/gin" +) + +type EmailRouter struct{} + +func (s *EmailRouter) InitEmailRouter(Router *gin.RouterGroup) { + emailRouter := Router.Use(middleware.OperationRecord()) + EmailApi := api.ApiGroupApp.EmailApi.EmailTest + SendEmail := api.ApiGroupApp.EmailApi.SendEmail + { + emailRouter.POST("emailTest", EmailApi) // 发送测试邮件 + emailRouter.POST("sendEmail", SendEmail) // 发送邮件 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/service/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/service/enter.go new file mode 100644 index 000000000..e96e267f5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/service/enter.go @@ -0,0 +1,7 @@ +package service + +type ServiceGroup struct { + EmailService +} + +var ServiceGroupApp = new(ServiceGroup) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/service/sys_email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/service/sys_email.go new file mode 100644 index 000000000..57042769c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/service/sys_email.go @@ -0,0 +1,32 @@ +package service + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/utils" +) + +type EmailService struct{} + +//@author: [maplepie](https://github.com/maplepie) +//@function: EmailTest +//@description: 发送邮件测试 +//@return: err error + +func (e *EmailService) EmailTest() (err error) { + subject := "test" + body := "test" + err = utils.EmailTest(subject, body) + return err +} + +//@author: [maplepie](https://github.com/maplepie) +//@function: EmailTest +//@description: 发送邮件测试 +//@return: err error +//@params to string 收件人 +//@params subject string 标题(主题) +//@params body string 邮件内容 + +func (e *EmailService) SendEmail(to, subject, body string) (err error) { + err = utils.Email(to, subject, body) + return err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/utils/email.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/utils/email.go new file mode 100644 index 000000000..aa82e1c89 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/email/utils/email.go @@ -0,0 +1,82 @@ +package utils + +import ( + "crypto/tls" + "fmt" + "net/smtp" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/global" + + "github.com/jordan-wright/email" +) + +//@author: [maplepie](https://github.com/maplepie) +//@function: Email +//@description: Email发送方法 +//@param: subject string, body string +//@return: error + +func Email(To, subject string, body string) error { + to := strings.Split(To, ",") + return send(to, subject, body) +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: ErrorToEmail +//@description: 给email中间件错误发送邮件到指定邮箱 +//@param: subject string, body string +//@return: error + +func ErrorToEmail(subject string, body string) error { + to := strings.Split(global.GlobalConfig.To, ",") + if to[len(to)-1] == "" { // 判断切片的最后一个元素是否为空,为空则移除 + to = to[:len(to)-1] + } + return send(to, subject, body) +} + +//@author: [maplepie](https://github.com/maplepie) +//@function: EmailTest +//@description: Email测试方法 +//@param: subject string, body string +//@return: error + +func EmailTest(subject string, body string) error { + to := []string{global.GlobalConfig.To} + return send(to, subject, body) +} + +//@author: [maplepie](https://github.com/maplepie) +//@function: send +//@description: Email发送方法 +//@param: subject string, body string +//@return: error + +func send(to []string, subject string, body string) error { + from := global.GlobalConfig.From + nickname := global.GlobalConfig.Nickname + secret := global.GlobalConfig.Secret + host := global.GlobalConfig.Host + port := global.GlobalConfig.Port + isSSL := global.GlobalConfig.IsSSL + + auth := smtp.PlainAuth("", from, secret, host) + e := email.NewEmail() + if nickname != "" { + e.From = fmt.Sprintf("%s <%s>", nickname, from) + } else { + e.From = from + } + e.To = to + e.Subject = subject + e.HTML = []byte(body) + var err error + hostAddr := fmt.Sprintf("%s:%d", host, port) + if isSSL { + err = e.SendWithTLS(hostAddr, auth, &tls.Config{ServerName: host}) + } else { + err = e.Send(hostAddr, auth) + } + return err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/plugin-tool/utils/check.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/plugin-tool/utils/check.go new file mode 100644 index 000000000..4ea21921e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/plugin/plugin-tool/utils/check.go @@ -0,0 +1,50 @@ +package utils + +import ( + "fmt" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +func RegisterApis(apis ...system.SysApi) { + var count int64 + var apiPaths []string + for i := range apis { + apiPaths = append(apiPaths, apis[i].Path) + } + global.GVA_DB.Find(&[]system.SysApi{}, "path in (?)", apiPaths).Count(&count) + if count > 0 { + return + } + err := global.GVA_DB.Create(&apis).Error + if err != nil { + fmt.Println(err) + } +} + +func RegisterMenus(menus ...system.SysBaseMenu) { + var count int64 + var menuNames []string + parentMenu := menus[0] + otherMenus := menus[1:] + for i := range menus { + menuNames = append(menuNames, menus[i].Name) + } + global.GVA_DB.Find(&[]system.SysBaseMenu{}, "name in (?)", menuNames).Count(&count) + if count > 0 { + return + } + err := global.GVA_DB.Create(&parentMenu).Error + if err != nil { + fmt.Println(err) + } + for i := range otherMenus { + pid := parentMenu.ID + otherMenus[i].ParentId = pid + } + err = global.GVA_DB.Create(&otherMenus).Error + if err != nil { + fmt.Println(err) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/api.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/api.go.tpl new file mode 100644 index 000000000..1d276cff6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/api.go.tpl @@ -0,0 +1,40 @@ +{{if .IsPlugin}} +// {{.FuncName}} {{.FuncDesc}} +// @Tags {{.StructName}} +// @Summary {{.FuncDesc}} +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}] +func (a *{{.Abbreviation}}) {{.FuncName}}(c *gin.Context) { + // 请添加自己的业务逻辑 + err := service{{ .StructName }}.{{.FuncName}}() + if err != nil { + global.GVA_LOG.Error("失败!", zap.Error(err)) + response.FailWithMessage("失败", c) + return + } + response.OkWithData("返回数据",c) +} + +{{- else -}} + +// {{.FuncName}} {{.FuncDesc}} +// @Tags {{.StructName}} +// @Summary {{.FuncDesc}} +// @accept application/json +// @Produce application/json +// @Param data query {{.Package}}Req.{{.StructName}}Search true "成功" +// @Success 200 {object} response.Response{data=object,msg=string} "成功" +// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}] +func ({{.Abbreviation}}Api *{{.StructName}}Api){{.FuncName}}(c *gin.Context) { + // 请添加自己的业务逻辑 + err := {{.Abbreviation}}Service.{{.FuncName}}() + if err != nil { + global.GVA_LOG.Error("失败!", zap.Error(err)) + response.FailWithMessage("失败", c) + return + } + response.OkWithData("返回数据",c) +} +{{end}} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/api.js.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/api.js.tpl new file mode 100644 index 000000000..5cc491fe3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/api.js.tpl @@ -0,0 +1,32 @@ +{{if .IsPlugin}} +// {{.FuncName}} {{.FuncDesc}} +// @Tags {{.StructName}} +// @Summary {{.FuncDesc}} +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}] +export const {{.Router}} = () => { + return service({ + url: '/{{.Abbreviation}}/{{.Router}}', + method: '{{.Method}}' + }) +} + +{{- else -}} + +// {{.FuncName}} {{.FuncDesc}} +// @Tags {{.StructName}} +// @Summary {{.FuncDesc}} +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=object,msg=string} "成功" +// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}] +export const {{.Router}} = () => { + return service({ + url: '/{{.Abbreviation}}/{{.Router}}', + method: '{{.Method}}' + }) +} + +{{- end -}} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/server.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/server.go.tpl new file mode 100644 index 000000000..1c5191c4b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/function/server.go.tpl @@ -0,0 +1,25 @@ +{{- $db := "" }} +{{- if eq .BusinessDB "" }} + {{- $db = "global.GVA_DB" }} +{{- else}} + {{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }} +{{- end}} +{{if .IsPlugin}} + +// {{.FuncName}} {{.FuncDesc}} +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) {{.FuncName}}() (err error) { + db := {{$db}}.Model(&model.{{.StructName}}{}) + return db.Error +} + +{{- else -}} + +// {{.FuncName}} {{.FuncDesc}} +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service){{.FuncName}}() (err error) { + // 请在这里实现自己的业务逻辑 + db := {{$db}}.Model(&{{.Package}}.{{.StructName}}{}) + return db.Error +} +{{end}} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/readme.txt.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/readme.txt.tpl new file mode 100644 index 000000000..25de23006 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/readme.txt.tpl @@ -0,0 +1,7 @@ +代码解压后把fe的api文件内容粘贴进前端api文件夹下并修改为自己想要的名字即可 + +后端代码解压后同理,放到自己想要的 mvc对应路径 并且到 initRouter中注册自动生成的路由 到registerTable中注册自动生成的model + +项目github:"https://github.com/piexlmax/github.com/flipped-aurora/gin-vue-admin/server" + +希望大家给个star多多鼓励 diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/api/api.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/api/api.go.tpl new file mode 100644 index 000000000..43e727aa1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/api/api.go.tpl @@ -0,0 +1,212 @@ +package {{.Package}} + +import ( + {{if not .OnlyTemplate}} + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}" + {{.Package}}Req "github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" + {{- if .AutoCreateResource}} + "github.com/flipped-aurora/gin-vue-admin/server/utils" + {{- end }} + {{- else}} + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/gin-gonic/gin" + {{- end}} +) + +type {{.StructName}}Api struct {} + +{{if not .OnlyTemplate}} + +// Create{{.StructName}} 创建{{.Description}} +// @Tags {{.StructName}} +// @Summary 创建{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body {{.Package}}.{{.StructName}} true "创建{{.Description}}" +// @Success 200 {object} response.Response{msg=string} "创建成功" +// @Router /{{.Abbreviation}}/create{{.StructName}} [post] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Create{{.StructName}}(c *gin.Context) { + var {{.Abbreviation}} {{.Package}}.{{.StructName}} + err := c.ShouldBindJSON(&{{.Abbreviation}}) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + {{- if .AutoCreateResource }} + {{.Abbreviation}}.CreatedBy = utils.GetUserID(c) + {{- end }} + err = {{.Abbreviation}}Service.Create{{.StructName}}(&{{.Abbreviation}}) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败:" + err.Error(), c) + return + } + response.OkWithMessage("创建成功", c) +} + +// Delete{{.StructName}} 删除{{.Description}} +// @Tags {{.StructName}} +// @Summary 删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body {{.Package}}.{{.StructName}} true "删除{{.Description}}" +// @Success 200 {object} response.Response{msg=string} "删除成功" +// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}(c *gin.Context) { + {{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}") + {{- if .AutoCreateResource }} + userID := utils.GetUserID(c) + {{- end }} + err := {{.Abbreviation}}Service.Delete{{.StructName}}({{.PrimaryField.FieldJson}} {{- if .AutoCreateResource -}},userID{{- end -}}) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败:" + err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// Delete{{.StructName}}ByIds 批量删除{{.Description}} +// @Tags {{.StructName}} +// @Summary 批量删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "批量删除成功" +// @Router /{{.Abbreviation}}/delete{{.StructName}}ByIds [delete] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}ByIds(c *gin.Context) { + {{.PrimaryField.FieldJson}}s := c.QueryArray("{{.PrimaryField.FieldJson}}s[]") + {{- if .AutoCreateResource }} + userID := utils.GetUserID(c) + {{- end }} + err := {{.Abbreviation}}Service.Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s{{- if .AutoCreateResource }},userID{{- end }}) + if err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败:" + err.Error(), c) + return + } + response.OkWithMessage("批量删除成功", c) +} + +// Update{{.StructName}} 更新{{.Description}} +// @Tags {{.StructName}} +// @Summary 更新{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body {{.Package}}.{{.StructName}} true "更新{{.Description}}" +// @Success 200 {object} response.Response{msg=string} "更新成功" +// @Router /{{.Abbreviation}}/update{{.StructName}} [put] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Update{{.StructName}}(c *gin.Context) { + var {{.Abbreviation}} {{.Package}}.{{.StructName}} + err := c.ShouldBindJSON(&{{.Abbreviation}}) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + {{- if .AutoCreateResource }} + {{.Abbreviation}}.UpdatedBy = utils.GetUserID(c) + {{- end }} + err = {{.Abbreviation}}Service.Update{{.StructName}}({{.Abbreviation}}) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败:" + err.Error(), c) + return + } + response.OkWithMessage("更新成功", c) +} + +// Find{{.StructName}} 用id查询{{.Description}} +// @Tags {{.StructName}} +// @Summary 用id查询{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query {{.Package}}.{{.StructName}} true "用id查询{{.Description}}" +// @Success 200 {object} response.Response{data={{.Package}}.{{.StructName}},msg=string} "查询成功" +// @Router /{{.Abbreviation}}/find{{.StructName}} [get] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Find{{.StructName}}(c *gin.Context) { + {{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}") + re{{.Abbreviation}}, err := {{.Abbreviation}}Service.Get{{.StructName}}({{.PrimaryField.FieldJson}}) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败:" + err.Error(), c) + return + } + response.OkWithData(re{{.Abbreviation}}, c) +} + +// Get{{.StructName}}List 分页获取{{.Description}}列表 +// @Tags {{.StructName}} +// @Summary 分页获取{{.Description}}列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}List [get] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Context) { + var pageInfo {{.Package}}Req.{{.StructName}}Search + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := {{.Abbreviation}}Service.Get{{.StructName}}InfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败:" + err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +{{- if .HasDataSource }} +// Get{{.StructName}}DataSource 获取{{.StructName}}的数据源 +// @Tags {{.StructName}} +// @Summary 获取{{.StructName}}的数据源 +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=object,msg=string} "查询成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}DataSource [get] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}DataSource(c *gin.Context) { + // 此接口为获取数据源定义的数据 + dataSource, err := {{.Abbreviation}}Service.Get{{.StructName}}DataSource() + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败:" + err.Error(), c) + return + } + response.OkWithData(dataSource, c) +} +{{- end }} + +{{- end }} + +// Get{{.StructName}}Public 不需要鉴权的{{.Description}}接口 +// @Tags {{.StructName}} +// @Summary 不需要鉴权的{{.Description}}接口 +// @accept application/json +// @Produce application/json +// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get] +func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}Public(c *gin.Context) { + // 此接口不需要鉴权 + // 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑 + {{.Abbreviation}}Service.Get{{.StructName}}Public() + response.OkWithDetailed(gin.H{ + "info": "不需要鉴权的{{.Description}}接口信息", + }, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/api/enter.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/api/enter.go.tpl new file mode 100644 index 000000000..778b3146e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/api/enter.go.tpl @@ -0,0 +1,4 @@ +package {{ .Package }} + +type ApiGroup struct { +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/model/model.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/model/model.go.tpl new file mode 100644 index 000000000..36d903754 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/model/model.go.tpl @@ -0,0 +1,60 @@ +// 自动生成模板{{.StructName}} +package {{.Package}} + +{{- if not .OnlyTemplate}} +import ( + {{- if .GvaModel }} + "github.com/flipped-aurora/gin-vue-admin/server/global" + {{- end }} + {{- if or .HasTimer }} + "time" + {{- end }} + {{- if .NeedJSON }} + "gorm.io/datatypes" + {{- end }} +) +{{- end }} + +// {{.Description}} 结构体 {{.StructName}} +type {{.StructName}} struct { +{{- if not .OnlyTemplate}} +{{- if .GvaModel }} + global.GVA_MODEL +{{- end }} +{{- range .Fields}} + {{- if eq .FieldType "enum" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "picture" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "video" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "file" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"array,object"` + {{- else if eq .FieldType "pictures" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"array,object"` + {{- else if eq .FieldType "richtext" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "json" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"object"` + {{- else if eq .FieldType "array" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"array,object"` + {{- else if ne .FieldType "string" }} + {{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- else }} + {{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- end }} {{ if .FieldDesc }}//{{.FieldDesc}} {{ end }} +{{- end }} + {{- if .AutoCreateResource }} + CreatedBy uint `gorm:"column:created_by;comment:创建者"` + UpdatedBy uint `gorm:"column:updated_by;comment:更新者"` + DeletedBy uint `gorm:"column:deleted_by;comment:删除者"` + {{- end }} +{{- end }} +} + +{{ if .TableName }} +// TableName {{.Description}} {{.StructName}}自定义表名 {{.TableName}} +func ({{.StructName}}) TableName() string { + return "{{.TableName}}" +} +{{ end }} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/model/request/request.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/model/request/request.go.tpl new file mode 100644 index 000000000..e97a0fd7a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/model/request/request.go.tpl @@ -0,0 +1,38 @@ +package request + +import ( +{{- if not .OnlyTemplate }} + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + {{ if or .HasSearchTimer .GvaModel}}"time"{{ end }} +{{- end }} +) + +type {{.StructName}}Search struct{ +{{- if not .OnlyTemplate}} +{{- if .GvaModel }} + StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"` + EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"` +{{- end }} +{{- range .Fields}} + {{- if ne .FieldSearchType ""}} + {{- if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}} + Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"` + End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"` + {{- else }} + {{- if or (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "pictures") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" ` + {{- else if ne .FieldType "string" }} + {{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" ` + {{- else }} + {{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" ` + {{- end }} + {{- end }} + {{- end}} +{{- end }} + request.PageInfo + {{- if .NeedSort}} + Sort string `json:"sort" form:"sort"` + Order string `json:"order" form:"order"` + {{- end}} +{{- end}} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/router/enter.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/router/enter.go.tpl new file mode 100644 index 000000000..178aecf3b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/router/enter.go.tpl @@ -0,0 +1,4 @@ +package {{ .Package }} + +type RouterGroup struct { +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/router/router.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/router/router.go.tpl new file mode 100644 index 000000000..9a4ba9de0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/router/router.go.tpl @@ -0,0 +1,42 @@ +package {{.Package}} + +import ( + {{if .OnlyTemplate}}// {{ end}}"github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type {{.StructName}}Router struct {} + +// Init{{.StructName}}Router 初始化 {{.Description}} 路由信息 +func (s *{{.StructName}}Router) Init{{.StructName}}Router(Router *gin.RouterGroup,PublicRouter *gin.RouterGroup) { + {{- if not .OnlyTemplate}} + {{.Abbreviation}}Router := Router.Group("{{.Abbreviation}}").Use(middleware.OperationRecord()) + {{.Abbreviation}}RouterWithoutRecord := Router.Group("{{.Abbreviation}}") + {{- else }} + // {{.Abbreviation}}Router := Router.Group("{{.Abbreviation}}").Use(middleware.OperationRecord()) + // {{.Abbreviation}}RouterWithoutRecord := Router.Group("{{.Abbreviation}}") + {{- end}} + {{.Abbreviation}}RouterWithoutAuth := PublicRouter.Group("{{.Abbreviation}}") + {{- if not .OnlyTemplate}} + { + {{.Abbreviation}}Router.POST("create{{.StructName}}", {{.Abbreviation}}Api.Create{{.StructName}}) // 新建{{.Description}} + {{.Abbreviation}}Router.DELETE("delete{{.StructName}}", {{.Abbreviation}}Api.Delete{{.StructName}}) // 删除{{.Description}} + {{.Abbreviation}}Router.DELETE("delete{{.StructName}}ByIds", {{.Abbreviation}}Api.Delete{{.StructName}}ByIds) // 批量删除{{.Description}} + {{.Abbreviation}}Router.PUT("update{{.StructName}}", {{.Abbreviation}}Api.Update{{.StructName}}) // 更新{{.Description}} + } + { + {{.Abbreviation}}RouterWithoutRecord.GET("find{{.StructName}}", {{.Abbreviation}}Api.Find{{.StructName}}) // 根据ID获取{{.Description}} + {{.Abbreviation}}RouterWithoutRecord.GET("get{{.StructName}}List", {{.Abbreviation}}Api.Get{{.StructName}}List) // 获取{{.Description}}列表 + } + { + {{- if .HasDataSource}} + {{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}DataSource", {{.Abbreviation}}Api.Get{{.StructName}}DataSource) // 获取{{.Description}}数据源 + {{- end}} + {{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}Public", {{.Abbreviation}}Api.Get{{.StructName}}Public) // {{.Description}}开放接口 + } + {{- else}} + { + {{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}Public", {{.Abbreviation}}Api.Get{{.StructName}}Public) // {{.Description}}开放接口 + } + {{ end }} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/service/enter.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/service/enter.go.tpl new file mode 100644 index 000000000..adf1db02e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/service/enter.go.tpl @@ -0,0 +1,4 @@ +package {{ .Package }} + +type ServiceGroup struct { +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/service/service.go.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/service/service.go.tpl new file mode 100644 index 000000000..1c44daf87 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/server/service/service.go.tpl @@ -0,0 +1,168 @@ +package {{.Package}} + +import ( +{{- if not .OnlyTemplate }} + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}" + {{.Package}}Req "github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}/request" + {{- if .AutoCreateResource }} + "gorm.io/gorm" + {{- end}} +{{- end }} +) + +type {{.StructName}}Service struct {} + +{{- $db := "" }} +{{- if eq .BusinessDB "" }} + {{- $db = "global.GVA_DB" }} +{{- else}} + {{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }} +{{- end}} + +{{- if not .OnlyTemplate }} +// Create{{.StructName}} 创建{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{.Abbreviation}} *{{.Package}}.{{.StructName}}) (err error) { + err = {{$db}}.Create({{.Abbreviation}}).Error + return err +} + +// Delete{{.StructName}} 删除{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}({{.PrimaryField.FieldJson}} string{{- if .AutoCreateResource -}},userID uint{{- end -}}) (err error) { + {{- if .AutoCreateResource }} + err = {{$db}}.Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).Update("deleted_by", userID).Error; err != nil { + return err + } + if err = tx.Delete(&{{.Package}}.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error; err != nil { + return err + } + return nil + }) + {{- else }} + err = {{$db}}.Delete(&{{.Package}}.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error + {{- end }} + return err +} + +// Delete{{.StructName}}ByIds 批量删除{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s []string {{- if .AutoCreateResource }},deleted_by uint{{- end}}) (err error) { + {{- if .AutoCreateResource }} + err = {{$db}}.Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Update("deleted_by", deleted_by).Error; err != nil { + return err + } + if err := tx.Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Delete(&{{.Package}}.{{.StructName}}{}).Error; err != nil { + return err + } + return nil + }) + {{- else}} + err = {{$db}}.Delete(&[]{{.Package}}.{{.StructName}}{},"{{.PrimaryField.ColumnName}} in ?",{{.PrimaryField.FieldJson}}s).Error + {{- end}} + return err +} + +// Update{{.StructName}} 更新{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service)Update{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) { + err = {{$db}}.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?",{{.Abbreviation}}.{{.PrimaryField.FieldName}}).Updates(&{{.Abbreviation}}).Error + return err +} + +// Get{{.StructName}} 根据{{.PrimaryField.FieldJson}}获取{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}({{.PrimaryField.FieldJson}} string) ({{.Abbreviation}} {{.Package}}.{{.StructName}}, err error) { + err = {{$db}}.Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).First(&{{.Abbreviation}}).Error + return +} + +// Get{{.StructName}}InfoList 分页获取{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoList(info {{.Package}}Req.{{.StructName}}Search) (list []{{.Package}}.{{.StructName}}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := {{$db}}.Model(&{{.Package}}.{{.StructName}}{}) + var {{.Abbreviation}}s []{{.Package}}.{{.StructName}} + // 如果有条件搜索 下方会自动创建搜索语句 +{{- if .GvaModel }} + if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil { + db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) + } +{{- end }} + {{- range .Fields}} + {{- if .FieldSearchType}} + {{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "pictures") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }} + if info.{{.FieldName}} != "" { + {{- if or (eq .FieldType "enum") (eq .FieldType "string") }} + db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }}) + {{- else}} + // 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务 + {{- end}} + } + {{- else if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}} + if info.Start{{.FieldName}} != nil && info.End{{.FieldName}} != nil { + db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ? AND ? ",info.Start{{.FieldName}},info.End{{.FieldName}}) + } + {{- else}} + if info.{{.FieldName}} != nil { + db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+{{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }}) + } + {{- end }} + {{- end }} + {{- end }} + err = db.Count(&total).Error + if err!=nil { + return + } + {{- if .NeedSort}} + var OrderStr string + orderMap := make(map[string]bool) + {{- range .Fields}} + {{- if .Sort}} + orderMap["{{.ColumnName}}"] = true + {{- end}} + {{- end}} + if orderMap[info.Sort] { + OrderStr = info.Sort + if info.Order == "descending" { + OrderStr = OrderStr + " desc" + } + db = db.Order(OrderStr) + } + {{- end}} + + if limit != 0 { + db = db.Limit(limit).Offset(offset) + } + + err = db.Find(&{{.Abbreviation}}s).Error + return {{.Abbreviation}}s, total, err +} + +{{- if .HasDataSource }} +func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}DataSource() (res map[string][]map[string]any, err error) { + res = make(map[string][]map[string]any) + {{range $key, $value := .DataSourceMap}} + {{$key}} := make([]map[string]any, 0) + {{ $dataDB := "" }} + {{- if eq $value.DBName "" }} + {{ $dataDB = $db }} + {{- else}} + {{ $dataDB = printf "global.MustGetGlobalDBByDBName(\"%s\")" $value.DBName }} + {{- end}} + {{$dataDB}}.Table("{{$value.Table}}").Select("{{$value.Label}} as label,{{$value.Value}} as value").Scan(&{{$key}}) + res["{{$key}}"] = {{$key}} + {{- end }} + return +} +{{- end }} +{{- end }} +func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}Public() { + // 此方法为获取数据源定义的数据 + // 请自行实现 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/api/api.js.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/api/api.js.tpl new file mode 100644 index 000000000..94085baa1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/api/api.js.tpl @@ -0,0 +1,130 @@ +import service from '@/utils/request' + +{{- if not .OnlyTemplate}} +// @Tags {{.StructName}} +// @Summary 创建{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "创建{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /{{.Abbreviation}}/create{{.StructName}} [post] +export const create{{.StructName}} = (data) => { + return service({ + url: '/{{.Abbreviation}}/create{{.StructName}}', + method: 'post', + data + }) +} + +// @Tags {{.StructName}} +// @Summary 删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "删除{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] +export const delete{{.StructName}} = (params) => { + return service({ + url: '/{{.Abbreviation}}/delete{{.StructName}}', + method: 'delete', + params + }) +} + +// @Tags {{.StructName}} +// @Summary 批量删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] +export const delete{{.StructName}}ByIds = (params) => { + return service({ + url: '/{{.Abbreviation}}/delete{{.StructName}}ByIds', + method: 'delete', + params + }) +} + +// @Tags {{.StructName}} +// @Summary 更新{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "更新{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /{{.Abbreviation}}/update{{.StructName}} [put] +export const update{{.StructName}} = (data) => { + return service({ + url: '/{{.Abbreviation}}/update{{.StructName}}', + method: 'put', + data + }) +} + +// @Tags {{.StructName}} +// @Summary 用id查询{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.{{.StructName}} true "用id查询{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /{{.Abbreviation}}/find{{.StructName}} [get] +export const find{{.StructName}} = (params) => { + return service({ + url: '/{{.Abbreviation}}/find{{.StructName}}', + method: 'get', + params + }) +} + +// @Tags {{.StructName}} +// @Summary 分页获取{{.Description}}列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取{{.Description}}列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /{{.Abbreviation}}/get{{.StructName}}List [get] +export const get{{.StructName}}List = (params) => { + return service({ + url: '/{{.Abbreviation}}/get{{.StructName}}List', + method: 'get', + params + }) +} + +{{- if .HasDataSource}} +// @Tags {{.StructName}} +// @Summary 获取数据源 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /{{.Abbreviation}}/find{{.StructName}}DataSource [get] +export const get{{.StructName}}DataSource = () => { + return service({ + url: '/{{.Abbreviation}}/get{{.StructName}}DataSource', + method: 'get', + }) +} +{{- end}} + +{{- end}} + +// @Tags {{.StructName}} +// @Summary 不需要鉴权的{{.Description}}接口 +// @accept application/json +// @Produce application/json +// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get] +export const get{{.StructName}}Public = () => { + return service({ + url: '/{{.Abbreviation}}/get{{.StructName}}Public', + method: 'get', + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/view/form.vue.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/view/form.vue.tpl new file mode 100644 index 000000000..6ad1a4369 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/view/form.vue.tpl @@ -0,0 +1,250 @@ +{{- if not .OnlyTemplate }} + + + + + +{{- else }} + + + +{{- end }} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/view/table.vue.tpl b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/view/table.vue.tpl new file mode 100644 index 000000000..556cb828f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/package/web/view/table.vue.tpl @@ -0,0 +1,909 @@ +{{- $global := . }} +{{- $templateID := printf "%s_%s" .Package .StructName }} +{{- if not .OnlyTemplate}} + + + + + +{{- else}} + + + +{{- end}} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/api/api.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/api/api.go.template new file mode 100644 index 000000000..03321c74f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/api/api.go.template @@ -0,0 +1,207 @@ +package api + +import ( +{{if not .OnlyTemplate}} + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{.Package}}/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{.Package}}/model/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" + {{- if .AutoCreateResource}} + "github.com/flipped-aurora/gin-vue-admin/server/utils" + {{- end }} +{{- else }} + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/gin-gonic/gin" +{{- end }} +) + +var {{.StructName}} = new({{.Abbreviation}}) + +type {{.Abbreviation}} struct {} +{{if not .OnlyTemplate}} +// Create{{.StructName}} 创建{{.Description}} +// @Tags {{.StructName}} +// @Summary 创建{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "创建{{.Description}}" +// @Success 200 {object} response.Response{msg=string} "创建成功" +// @Router /{{.Abbreviation}}/create{{.StructName}} [post] +func (a *{{.Abbreviation}}) Create{{.StructName}}(c *gin.Context) { + var info model.{{.StructName}} + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + {{- if .AutoCreateResource }} + info.CreatedBy = utils.GetUserID(c) + {{- end }} + err = service{{ .StructName }}.Create{{.StructName}}(&info) + if err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败:" + err.Error(), c) + return + } + response.OkWithMessage("创建成功", c) +} + +// Delete{{.StructName}} 删除{{.Description}} +// @Tags {{.StructName}} +// @Summary 删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "删除{{.Description}}" +// @Success 200 {object} response.Response{msg=string} "删除成功" +// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] +func (a *{{.Abbreviation}}) Delete{{.StructName}}(c *gin.Context) { + {{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}") +{{- if .AutoCreateResource }} + userID := utils.GetUserID(c) +{{- end }} + err := service{{ .StructName }}.Delete{{.StructName}}({{.PrimaryField.FieldJson}} {{- if .AutoCreateResource -}},userID{{- end -}}) + if err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败:" + err.Error(), c) + return + } + response.OkWithMessage("删除成功", c) +} + +// Delete{{.StructName}}ByIds 批量删除{{.Description}} +// @Tags {{.StructName}} +// @Summary 批量删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "批量删除成功" +// @Router /{{.Abbreviation}}/delete{{.StructName}}ByIds [delete] +func (a *{{.Abbreviation}}) Delete{{.StructName}}ByIds(c *gin.Context) { + {{.PrimaryField.FieldJson}}s := c.QueryArray("{{.PrimaryField.FieldJson}}s[]") +{{- if .AutoCreateResource }} + userID := utils.GetUserID(c) +{{- end }} + err := service{{ .StructName }}.Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s{{- if .AutoCreateResource }},userID{{- end }}) + if err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败:" + err.Error(), c) + return + } + response.OkWithMessage("批量删除成功", c) +} + +// Update{{.StructName}} 更新{{.Description}} +// @Tags {{.StructName}} +// @Summary 更新{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "更新{{.Description}}" +// @Success 200 {object} response.Response{msg=string} "更新成功" +// @Router /{{.Abbreviation}}/update{{.StructName}} [put] +func (a *{{.Abbreviation}}) Update{{.StructName}}(c *gin.Context) { + var info model.{{.StructName}} + err := c.ShouldBindJSON(&info) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } +{{- if .AutoCreateResource }} + info.UpdatedBy = utils.GetUserID(c) +{{- end }} + err = service{{ .StructName }}.Update{{.StructName}}(info) + if err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败:" + err.Error(), c) + return + } + response.OkWithMessage("更新成功", c) +} + +// Find{{.StructName}} 用id查询{{.Description}} +// @Tags {{.StructName}} +// @Summary 用id查询{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.{{.StructName}} true "用id查询{{.Description}}" +// @Success 200 {object} response.Response{data=model.{{.StructName}},msg=string} "查询成功" +// @Router /{{.Abbreviation}}/find{{.StructName}} [get] +func (a *{{.Abbreviation}}) Find{{.StructName}}(c *gin.Context) { + {{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}") + re{{.Abbreviation}}, err := service{{ .StructName }}.Get{{.StructName}}({{.PrimaryField.FieldJson}}) + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败:" + err.Error(), c) + return + } + response.OkWithData(re{{.Abbreviation}}, c) +} + +// Get{{.StructName}}List 分页获取{{.Description}}列表 +// @Tags {{.StructName}} +// @Summary 分页获取{{.Description}}列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.{{.StructName}}Search true "分页获取{{.Description}}列表" +// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}List [get] +func (a *{{.Abbreviation}}) Get{{.StructName}}List(c *gin.Context) { + var pageInfo request.{{.StructName}}Search + err := c.ShouldBindQuery(&pageInfo) + if err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := service{{ .StructName }}.Get{{.StructName}}InfoList(pageInfo) + if err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败:" + err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) +} + +{{- if .HasDataSource }} +// Get{{.StructName}}DataSource 获取{{.StructName}}的数据源 +// @Tags {{.StructName}} +// @Summary 获取{{.StructName}}的数据源 +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{data=object,msg=string} "查询成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}DataSource [get] +func (a *{{.Abbreviation}}) Get{{.StructName}}DataSource(c *gin.Context) { + // 此接口为获取数据源定义的数据 + dataSource, err := service{{ .StructName }}.Get{{.StructName}}DataSource() + if err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败:" + err.Error(), c) + return + } + response.OkWithData(dataSource, c) +} +{{- end }} +{{- end }} +// Get{{.StructName}}Public 不需要鉴权的{{.Description}}接口 +// @Tags {{.StructName}} +// @Summary 不需要鉴权的{{.Description}}接口 +// @accept application/json +// @Produce application/json +// @Param data query request.{{.StructName}}Search true "分页获取{{.Description}}列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get] +func (a *{{.Abbreviation}}) Get{{.StructName}}Public(c *gin.Context) { + // 此接口不需要鉴权 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑 + service{{ .StructName }}.Get{{.StructName}}Public() + response.OkWithDetailed(gin.H{"info": "不需要鉴权的{{.Description}}接口信息"}, "获取成功", c) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/api/enter.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/api/enter.go.template new file mode 100644 index 000000000..989fb3507 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/api/enter.go.template @@ -0,0 +1,6 @@ +package api + +var Api = new(api) + +type api struct { +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/config/config.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/config/config.go.template new file mode 100644 index 000000000..809bc990f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/config/config.go.template @@ -0,0 +1,4 @@ +package config + +type Config struct { +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/gen/gen.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/gen/gen.go.template new file mode 100644 index 000000000..5639d4ab3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/gen/gen.go.template @@ -0,0 +1,18 @@ +package main + +import ( + "gorm.io/gen" + "path/filepath" +) + +//go:generate go mod tidy +//go:generate go mod download +//go:generate go run gen.go +func main() { + g := gen.NewGenerator(gen.Config{ + OutPath: filepath.Join("..", "..", "..", "{{ .Package }}", "blender", "model", "dao"), + Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, + }) + g.ApplyBasic() + g.Execute() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/api.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/api.go.template new file mode 100644 index 000000000..adf5d64cf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/api.go.template @@ -0,0 +1,12 @@ +package initialize + +import ( + "context" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" +) + +func Api(ctx context.Context) { + entities := []model.SysApi{} + utils.RegisterApis(entities...) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/gorm.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/gorm.go.template new file mode 100644 index 000000000..0988bccff --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/gorm.go.template @@ -0,0 +1,18 @@ +package initialize + +import ( + "context" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + + "github.com/pkg/errors" + "go.uber.org/zap" +) + +func Gorm(ctx context.Context) { + err := global.GVA_DB.WithContext(ctx).AutoMigrate() + if err != nil { + err = errors.Wrap(err, "注册表失败!") + zap.L().Error(fmt.Sprintf("%+v", err)) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/menu.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/menu.go.template new file mode 100644 index 000000000..06993db3d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/menu.go.template @@ -0,0 +1,12 @@ +package initialize + +import ( + "context" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" +) + +func Menu(ctx context.Context) { + entities := []model.SysBaseMenu{} + utils.RegisterMenus(entities...) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/router.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/router.go.template new file mode 100644 index 000000000..f7f8ed0fb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/router.go.template @@ -0,0 +1,14 @@ +package initialize + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +func Router(engine *gin.Engine) { + public := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("") + public.Use() + private := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("") + private.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/viper.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/viper.go.template new file mode 100644 index 000000000..a1887ac28 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/initialize/viper.go.template @@ -0,0 +1,17 @@ +package initialize + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Package }}/plugin" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +func Viper() { + err := global.GVA_VP.UnmarshalKey("{{ .Package }}", &plugin.Config) + if err != nil { + err = errors.Wrap(err, "初始化配置文件失败!") + zap.L().Error(fmt.Sprintf("%+v", err)) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/model/model.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/model/model.go.template new file mode 100644 index 000000000..153ab3ba1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/model/model.go.template @@ -0,0 +1,59 @@ +package model + +{{- if not .OnlyTemplate}} +import ( + {{- if .GvaModel }} + "github.com/flipped-aurora/gin-vue-admin/server/global" + {{- end }} + {{- if or .HasTimer }} + "time" + {{- end }} + {{- if .NeedJSON }} + "gorm.io/datatypes" + {{- end }} +) +{{- end }} + +// {{.StructName}} {{.Description}} 结构体 +type {{.StructName}} struct { +{{- if not .OnlyTemplate}} +{{- if .GvaModel }} + global.GVA_MODEL +{{- end }} +{{- range .Fields}} + {{- if eq .FieldType "enum" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "picture" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "video" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "file" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"array,object"` + {{- else if eq .FieldType "pictures" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"array,object"` + {{- else if eq .FieldType "richtext" }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}` + {{- else if eq .FieldType "json" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"object"` + {{- else if eq .FieldType "array" }} + {{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}} swaggertype:"array,object"` + {{- else if ne .FieldType "string" }} + {{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- else }} + {{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if ne .FieldIndexType "" -}}{{ .FieldIndexType }};{{- end -}}{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}` + {{- end }} {{ if .FieldDesc }}//{{.FieldDesc}}{{ end }} +{{- end }} + {{- if .AutoCreateResource }} + CreatedBy uint `gorm:"column:created_by;comment:创建者"` + UpdatedBy uint `gorm:"column:updated_by;comment:更新者"` + DeletedBy uint `gorm:"column:deleted_by;comment:删除者"` + {{- end }} + {{- end }} +} + +{{ if .TableName }} +// TableName {{.Description}} {{.StructName}}自定义表名 {{.TableName}} +func ({{.StructName}}) TableName() string { + return "{{.TableName}}" +} +{{ end }} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/model/request/request.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/model/request/request.go.template new file mode 100644 index 000000000..cf2a56944 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/model/request/request.go.template @@ -0,0 +1,37 @@ +package request +{{- if not .OnlyTemplate}} +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + {{ if or .HasSearchTimer .GvaModel}}"time"{{ end }} +) +{{- end}} +type {{.StructName}}Search struct{ +{{- if not .OnlyTemplate}} + +{{- if .GvaModel }} + StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"` + EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"` +{{- end }} +{{- range .Fields}} + {{- if ne .FieldSearchType ""}} + {{- if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}} + Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"` + End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"` + {{- else }} + {{- if or (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "pictures") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }} + {{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" ` + {{- else if ne .FieldType "string" }} + {{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" ` + {{- else }} + {{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" ` + {{- end }} + {{- end }} + {{- end}} +{{- end }} + request.PageInfo + {{- if .NeedSort}} + Sort string `json:"sort" form:"sort"` + Order string `json:"order" form:"order"` + {{- end}} +{{- end}} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/plugin.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/plugin.go.template new file mode 100644 index 000000000..42c59f599 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/plugin.go.template @@ -0,0 +1,26 @@ +package {{ .Package }} + +import ( + "context" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Package }}/initialize" + interfaces "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin/v2" + "github.com/gin-gonic/gin" +) + +var _ interfaces.Plugin = (*plugin)(nil) + +var Plugin = new(plugin) + +type plugin struct{} + +// 如果需要配置文件,请到config.Config中填充配置结构,且到下方发放中填入其在config.yaml中的key并添加如下方法 +// initialize.Viper() +// 安装插件时候自动注册的api数据请到下方法.Api方法中实现并添加如下方法 +// initialize.Api(ctx) +// 安装插件时候自动注册的api数据请到下方法.Menu方法中实现并添加如下方法 +// initialize.Menu(ctx) +func (p *plugin) Register(group *gin.Engine) { + ctx := context.Background() + initialize.Gorm(ctx) + initialize.Router(group) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/plugin/plugin.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/plugin/plugin.go.template new file mode 100644 index 000000000..9129584bb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/plugin/plugin.go.template @@ -0,0 +1,5 @@ +package plugin + +import "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Package }}/config" + +var Config config.Config diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/router/enter.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/router/enter.go.template new file mode 100644 index 000000000..78517b371 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/router/enter.go.template @@ -0,0 +1,6 @@ +package router + +var Router = new(router) + +type router struct { +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/router/router.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/router/router.go.template new file mode 100644 index 000000000..cc5cd2677 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/router/router.go.template @@ -0,0 +1,46 @@ +package router + +import ( + {{if .OnlyTemplate }} // {{end}}"github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +var {{.StructName}} = new({{.Abbreviation}}) + +type {{.Abbreviation}} struct {} + +// Init 初始化 {{.Description}} 路由信息 +func (r *{{.Abbreviation}}) Init(public *gin.RouterGroup, private *gin.RouterGroup) { +{{- if not .OnlyTemplate }} + { + group := private.Group("{{.Abbreviation}}").Use(middleware.OperationRecord()) + group.POST("create{{.StructName}}", api{{.StructName}}.Create{{.StructName}}) // 新建{{.Description}} + group.DELETE("delete{{.StructName}}", api{{.StructName}}.Delete{{.StructName}}) // 删除{{.Description}} + group.DELETE("delete{{.StructName}}ByIds", api{{.StructName}}.Delete{{.StructName}}ByIds) // 批量删除{{.Description}} + group.PUT("update{{.StructName}}", api{{.StructName}}.Update{{.StructName}}) // 更新{{.Description}} + } + { + group := private.Group("{{.Abbreviation}}") + group.GET("find{{.StructName}}", api{{.StructName}}.Find{{.StructName}}) // 根据ID获取{{.Description}} + group.GET("get{{.StructName}}List", api{{.StructName}}.Get{{.StructName}}List) // 获取{{.Description}}列表 + } + { + group := public.Group("{{.Abbreviation}}") + {{- if .HasDataSource}} + group.GET("get{{.StructName}}DataSource", api{{.StructName}}.Get{{.StructName}}DataSource) // 获取{{.Description}}数据源 + {{- end}} + group.GET("get{{.StructName}}Public", api{{.StructName}}.Get{{.StructName}}Public) // {{.Description}}开放接口 + } +{{- else}} + // { + // group := private.Group("{{.Abbreviation}}").Use(middleware.OperationRecord()) + // } + // { + // group := private.Group("{{.Abbreviation}}") + // } + { + group := public.Group("{{.Abbreviation}}") + group.GET("get{{.StructName}}Public", api{{.StructName}}.Get{{.StructName}}Public) // {{.Description}}开放接口 + } +{{- end}} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/service/enter.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/service/enter.go.template new file mode 100644 index 000000000..034facbaf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/service/enter.go.template @@ -0,0 +1,7 @@ +package service + +var Service = new(service) + +type service struct { +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/service/service.go.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/service/service.go.template new file mode 100644 index 000000000..e62426d44 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/server/service/service.go.template @@ -0,0 +1,168 @@ +package service + +import ( +{{- if not .OnlyTemplate }} + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{.Package}}/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/{{.Package}}/model/request" + {{- if .AutoCreateResource }} + "gorm.io/gorm" + {{- end}} +{{- end }} +) + +var {{.StructName}} = new({{.Abbreviation}}) + +type {{.Abbreviation}} struct {} + +{{- $db := "" }} +{{- if eq .BusinessDB "" }} + {{- $db = "global.GVA_DB" }} +{{- else}} + {{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }} +{{- end}} +{{- if not .OnlyTemplate }} +// Create{{.StructName}} 创建{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) Create{{.StructName}}({{.Abbreviation}} *model.{{.StructName}}) (err error) { + err = {{$db}}.Create({{.Abbreviation}}).Error + return err +} + +// Delete{{.StructName}} 删除{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) Delete{{.StructName}}({{.PrimaryField.FieldJson}} string{{- if .AutoCreateResource -}},userID uint{{- end -}}) (err error) { + {{- if .AutoCreateResource }} + err = {{$db}}.Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&model.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).Update("deleted_by", userID).Error; err != nil { + return err + } + if err = tx.Delete(&model.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error; err != nil { + return err + } + return nil + }) + {{- else }} + err = {{$db}}.Delete(&model.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error + {{- end }} + return err +} + +// Delete{{.StructName}}ByIds 批量删除{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s []string {{- if .AutoCreateResource }},deleted_by uint{{- end}}) (err error) { + {{- if .AutoCreateResource }} + err = {{$db}}.Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&model.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Update("deleted_by", deleted_by).Error; err != nil { + return err + } + if err := tx.Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Delete(&model.{{.StructName}}{}).Error; err != nil { + return err + } + return nil + }) + {{- else}} + err = {{$db}}.Delete(&[]model.{{.StructName}}{},"{{.PrimaryField.ColumnName}} in ?",{{.PrimaryField.FieldJson}}s).Error + {{- end}} + return err +} + +// Update{{.StructName}} 更新{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) Update{{.StructName}}({{.Abbreviation}} model.{{.StructName}}) (err error) { + err = {{$db}}.Model(&model.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?",{{.Abbreviation}}.{{.PrimaryField.FieldName}}).Updates(&{{.Abbreviation}}).Error + return err +} + +// Get{{.StructName}} 根据{{.PrimaryField.FieldJson}}获取{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) Get{{.StructName}}({{.PrimaryField.FieldJson}} string) ({{.Abbreviation}} model.{{.StructName}}, err error) { + err = {{$db}}.Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).First(&{{.Abbreviation}}).Error + return +} + +// Get{{.StructName}}InfoList 分页获取{{.Description}}记录 +// Author [yourname](https://github.com/yourname) +func (s *{{.Abbreviation}}) Get{{.StructName}}InfoList(info request.{{.StructName}}Search) (list []model.{{.StructName}}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := {{$db}}.Model(&model.{{.StructName}}{}) + var {{.Abbreviation}}s []model.{{.StructName}} + // 如果有条件搜索 下方会自动创建搜索语句 +{{- if .GvaModel }} + if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil { + db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) + } +{{- end }} + {{- range .Fields}} + {{- if .FieldSearchType}} + {{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "pictures") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }} + if info.{{.FieldName}} != "" { + {{- if or (eq .FieldType "enum") (eq .FieldType "string") }} + db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }}) + {{- else}} + // 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务 + {{- end}} + } + {{- else if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}} + if info.Start{{.FieldName}} != nil && info.End{{.FieldName}} != nil { + db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ? AND ? ",info.Start{{.FieldName}},info.End{{.FieldName}}) + } + {{- else}} + if info.{{.FieldName}} != nil { + db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+{{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }}) + } + {{- end }} + {{- end }} + {{- end }} + err = db.Count(&total).Error + if err!=nil { + return + } + {{- if .NeedSort}} + var OrderStr string + orderMap := make(map[string]bool) + {{- range .Fields}} + {{- if .Sort}} + orderMap["{{.ColumnName}}"] = true + {{- end}} + {{- end}} + if orderMap[info.Sort] { + OrderStr = info.Sort + if info.Order == "descending" { + OrderStr = OrderStr + " desc" + } + db = db.Order(OrderStr) + } + {{- end}} + + if limit != 0 { + db = db.Limit(limit).Offset(offset) + } + err = db.Find(&{{.Abbreviation}}s).Error + return {{.Abbreviation}}s, total, err +} + +{{- if .HasDataSource }} +func (s *{{.Abbreviation}})Get{{.StructName}}DataSource() (res map[string][]map[string]any, err error) { + res = make(map[string][]map[string]any) + {{range $key, $value := .DataSourceMap}} + {{$key}} := make([]map[string]any, 0) + {{ $dataDB := "" }} + {{- if eq $value.DBName "" }} + {{ $dataDB = $db }} + {{- else}} + {{ $dataDB = printf "global.MustGetGlobalDBByDBName(\"%s\")" $value.DBName }} + {{- end}} + {{$dataDB}}.Table("{{$value.Table}}").Select("{{$value.Label}} as label,{{$value.Value}} as value").Scan(&{{$key}}) + res["{{$key}}"] = {{$key}} + {{- end }} + return +} +{{- end }} +{{- end }} + +func (s *{{.Abbreviation}})Get{{.StructName}}Public() { + +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/api/api.js.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/api/api.js.template new file mode 100644 index 000000000..208f386e4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/api/api.js.template @@ -0,0 +1,127 @@ +import service from '@/utils/request' +{{- if not .OnlyTemplate}} +// @Tags {{.StructName}} +// @Summary 创建{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "创建{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /{{.Abbreviation}}/create{{.StructName}} [post] +export const create{{.StructName}} = (data) => { + return service({ + url: '/{{.Abbreviation}}/create{{.StructName}}', + method: 'post', + data + }) +} + +// @Tags {{.StructName}} +// @Summary 删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "删除{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] +export const delete{{.StructName}} = (params) => { + return service({ + url: '/{{.Abbreviation}}/delete{{.StructName}}', + method: 'delete', + params + }) +} + +// @Tags {{.StructName}} +// @Summary 批量删除{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete] +export const delete{{.StructName}}ByIds = (params) => { + return service({ + url: '/{{.Abbreviation}}/delete{{.StructName}}ByIds', + method: 'delete', + params + }) +} + +// @Tags {{.StructName}} +// @Summary 更新{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.{{.StructName}} true "更新{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /{{.Abbreviation}}/update{{.StructName}} [put] +export const update{{.StructName}} = (data) => { + return service({ + url: '/{{.Abbreviation}}/update{{.StructName}}', + method: 'put', + data + }) +} + +// @Tags {{.StructName}} +// @Summary 用id查询{{.Description}} +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.{{.StructName}} true "用id查询{{.Description}}" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /{{.Abbreviation}}/find{{.StructName}} [get] +export const find{{.StructName}} = (params) => { + return service({ + url: '/{{.Abbreviation}}/find{{.StructName}}', + method: 'get', + params + }) +} + +// @Tags {{.StructName}} +// @Summary 分页获取{{.Description}}列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取{{.Description}}列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /{{.Abbreviation}}/get{{.StructName}}List [get] +export const get{{.StructName}}List = (params) => { + return service({ + url: '/{{.Abbreviation}}/get{{.StructName}}List', + method: 'get', + params + }) +} + +{{- if .HasDataSource}} +// @Tags {{.StructName}} +// @Summary 获取数据源 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /{{.Abbreviation}}/find{{.StructName}}DataSource [get] +export const get{{.StructName}}DataSource = () => { + return service({ + url: '/{{.Abbreviation}}/get{{.StructName}}DataSource', + method: 'get', + }) +} +{{- end}} +{{- end}} +// @Tags {{.StructName}} +// @Summary 不需要鉴权的{{.Description}}接口 +// @accept application/json +// @Produce application/json +// @Param data query request.{{.StructName}}Search true "分页获取{{.Description}}列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get] +export const get{{.StructName}}Public = () => { + return service({ + url: '/{{.Abbreviation}}/get{{.StructName}}Public', + method: 'get', + }) +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/form/form.vue.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/form/form.vue.template new file mode 100644 index 000000000..882501e78 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/form/form.vue.template @@ -0,0 +1,251 @@ +{{- if not .OnlyTemplate}} + + + + + +{{- else }} + + + +{{- end }} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/view/view.vue.template b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/view/view.vue.template new file mode 100644 index 000000000..05a5fb924 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/resource/plugin/web/view/view.vue.template @@ -0,0 +1,915 @@ +{{- $global := . }} +{{- $templateID := printf "%s_%s" .Package .StructName }} +{{- if not .OnlyTemplate}} + + + + + +{{- else}} + + + +{{- end}} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/enter.go new file mode 100644 index 000000000..6e6d81166 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/enter.go @@ -0,0 +1,13 @@ +package router + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/router/example" + "github.com/flipped-aurora/gin-vue-admin/server/router/system" +) + +var RouterGroupApp = new(RouterGroup) + +type RouterGroup struct { + System system.RouterGroup + Example example.RouterGroup +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/enter.go new file mode 100644 index 000000000..ce87aa2f9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/enter.go @@ -0,0 +1,15 @@ +package example + +import ( + api "github.com/flipped-aurora/gin-vue-admin/server/api/v1" +) + +type RouterGroup struct { + CustomerRouter + FileUploadAndDownloadRouter +} + +var ( + exaCustomerApi = api.ApiGroupApp.ExampleApiGroup.CustomerApi + exaFileUploadAndDownloadApi = api.ApiGroupApp.ExampleApiGroup.FileUploadAndDownloadApi +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/exa_customer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/exa_customer.go new file mode 100644 index 000000000..acdf3c7e5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/exa_customer.go @@ -0,0 +1,22 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type CustomerRouter struct{} + +func (e *CustomerRouter) InitCustomerRouter(Router *gin.RouterGroup) { + customerRouter := Router.Group("customer").Use(middleware.OperationRecord()) + customerRouterWithoutRecord := Router.Group("customer") + { + customerRouter.POST("customer", exaCustomerApi.CreateExaCustomer) // 创建客户 + customerRouter.PUT("customer", exaCustomerApi.UpdateExaCustomer) // 更新客户 + customerRouter.DELETE("customer", exaCustomerApi.DeleteExaCustomer) // 删除客户 + } + { + customerRouterWithoutRecord.GET("customer", exaCustomerApi.GetExaCustomer) // 获取单一客户信息 + customerRouterWithoutRecord.GET("customerList", exaCustomerApi.GetExaCustomerList) // 获取客户列表 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/exa_file_upload_and_download.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/exa_file_upload_and_download.go new file mode 100644 index 000000000..84f6ecdb0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/example/exa_file_upload_and_download.go @@ -0,0 +1,22 @@ +package example + +import ( + "github.com/gin-gonic/gin" +) + +type FileUploadAndDownloadRouter struct{} + +func (e *FileUploadAndDownloadRouter) InitFileUploadAndDownloadRouter(Router *gin.RouterGroup) { + fileUploadAndDownloadRouter := Router.Group("fileUploadAndDownload") + { + fileUploadAndDownloadRouter.POST("upload", exaFileUploadAndDownloadApi.UploadFile) // 上传文件 + fileUploadAndDownloadRouter.POST("getFileList", exaFileUploadAndDownloadApi.GetFileList) // 获取上传文件列表 + fileUploadAndDownloadRouter.POST("deleteFile", exaFileUploadAndDownloadApi.DeleteFile) // 删除指定文件 + fileUploadAndDownloadRouter.POST("editFileName", exaFileUploadAndDownloadApi.EditFileName) // 编辑文件名或者备注 + fileUploadAndDownloadRouter.POST("breakpointContinue", exaFileUploadAndDownloadApi.BreakpointContinue) // 断点续传 + fileUploadAndDownloadRouter.GET("findFile", exaFileUploadAndDownloadApi.FindFile) // 查询当前文件成功的切片 + fileUploadAndDownloadRouter.POST("breakpointContinueFinish", exaFileUploadAndDownloadApi.BreakpointContinueFinish) // 切片传输完成 + fileUploadAndDownloadRouter.POST("removeChunk", exaFileUploadAndDownloadApi.RemoveChunk) // 删除切片 + fileUploadAndDownloadRouter.POST("importURL", exaFileUploadAndDownloadApi.ImportURL) // 导入URL + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/enter.go new file mode 100644 index 000000000..7127d9e9f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/enter.go @@ -0,0 +1,44 @@ +package system + +import api "github.com/flipped-aurora/gin-vue-admin/server/api/v1" + +type RouterGroup struct { + ApiRouter + JwtRouter + SysRouter + BaseRouter + InitRouter + MenuRouter + UserRouter + CasbinRouter + AutoCodeRouter + AuthorityRouter + DictionaryRouter + OperationRecordRouter + DictionaryDetailRouter + AuthorityBtnRouter + SysExportTemplateRouter + SysParamsRouter +} + +var ( + dbApi = api.ApiGroupApp.SystemApiGroup.DBApi + jwtApi = api.ApiGroupApp.SystemApiGroup.JwtApi + baseApi = api.ApiGroupApp.SystemApiGroup.BaseApi + casbinApi = api.ApiGroupApp.SystemApiGroup.CasbinApi + systemApi = api.ApiGroupApp.SystemApiGroup.SystemApi + sysParamsApi = api.ApiGroupApp.SystemApiGroup.SysParamsApi + autoCodeApi = api.ApiGroupApp.SystemApiGroup.AutoCodeApi + authorityApi = api.ApiGroupApp.SystemApiGroup.AuthorityApi + apiRouterApi = api.ApiGroupApp.SystemApiGroup.SystemApiApi + dictionaryApi = api.ApiGroupApp.SystemApiGroup.DictionaryApi + authorityBtnApi = api.ApiGroupApp.SystemApiGroup.AuthorityBtnApi + authorityMenuApi = api.ApiGroupApp.SystemApiGroup.AuthorityMenuApi + autoCodePluginApi = api.ApiGroupApp.SystemApiGroup.AutoCodePluginApi + autocodeHistoryApi = api.ApiGroupApp.SystemApiGroup.AutoCodeHistoryApi + operationRecordApi = api.ApiGroupApp.SystemApiGroup.OperationRecordApi + autoCodePackageApi = api.ApiGroupApp.SystemApiGroup.AutoCodePackageApi + dictionaryDetailApi = api.ApiGroupApp.SystemApiGroup.DictionaryDetailApi + autoCodeTemplateApi = api.ApiGroupApp.SystemApiGroup.AutoCodeTemplateApi + exportTemplateApi = api.ApiGroupApp.SystemApiGroup.SysExportTemplateApi +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_api.go new file mode 100644 index 000000000..c98785e94 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_api.go @@ -0,0 +1,33 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type ApiRouter struct{} + +func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup, RouterPub *gin.RouterGroup) { + apiRouter := Router.Group("api").Use(middleware.OperationRecord()) + apiRouterWithoutRecord := Router.Group("api") + + apiPublicRouterWithoutRecord := RouterPub.Group("api") + { + apiRouter.GET("getApiGroups", apiRouterApi.GetApiGroups) // 获取路由组 + apiRouter.GET("syncApi", apiRouterApi.SyncApi) // 同步Api + apiRouter.POST("ignoreApi", apiRouterApi.IgnoreApi) // 忽略Api + apiRouter.POST("enterSyncApi", apiRouterApi.EnterSyncApi) // 确认同步Api + apiRouter.POST("createApi", apiRouterApi.CreateApi) // 创建Api + apiRouter.POST("deleteApi", apiRouterApi.DeleteApi) // 删除Api + apiRouter.POST("getApiById", apiRouterApi.GetApiById) // 获取单条Api消息 + apiRouter.POST("updateApi", apiRouterApi.UpdateApi) // 更新api + apiRouter.DELETE("deleteApisByIds", apiRouterApi.DeleteApisByIds) // 删除选中api + } + { + apiRouterWithoutRecord.POST("getAllApis", apiRouterApi.GetAllApis) // 获取所有api + apiRouterWithoutRecord.POST("getApiList", apiRouterApi.GetApiList) // 获取Api列表 + } + { + apiPublicRouterWithoutRecord.GET("freshCasbin", apiRouterApi.FreshCasbin) // 刷新casbin权限 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_authority.go new file mode 100644 index 000000000..9bef92ff1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_authority.go @@ -0,0 +1,23 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type AuthorityRouter struct{} + +func (s *AuthorityRouter) InitAuthorityRouter(Router *gin.RouterGroup) { + authorityRouter := Router.Group("authority").Use(middleware.OperationRecord()) + authorityRouterWithoutRecord := Router.Group("authority") + { + authorityRouter.POST("createAuthority", authorityApi.CreateAuthority) // 创建角色 + authorityRouter.POST("deleteAuthority", authorityApi.DeleteAuthority) // 删除角色 + authorityRouter.PUT("updateAuthority", authorityApi.UpdateAuthority) // 更新角色 + authorityRouter.POST("copyAuthority", authorityApi.CopyAuthority) // 拷贝角色 + authorityRouter.POST("setDataAuthority", authorityApi.SetDataAuthority) // 设置角色资源权限 + } + { + authorityRouterWithoutRecord.POST("getAuthorityList", authorityApi.GetAuthorityList) // 获取角色列表 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_authority_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_authority_btn.go new file mode 100644 index 000000000..370db85f8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_authority_btn.go @@ -0,0 +1,19 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type AuthorityBtnRouter struct{} + +var AuthorityBtnRouterApp = new(AuthorityBtnRouter) + +func (s *AuthorityBtnRouter) InitAuthorityBtnRouterRouter(Router *gin.RouterGroup) { + // authorityRouter := Router.Group("authorityBtn").Use(middleware.OperationRecord()) + authorityRouterWithoutRecord := Router.Group("authorityBtn") + { + authorityRouterWithoutRecord.POST("getAuthorityBtn", authorityBtnApi.GetAuthorityBtn) + authorityRouterWithoutRecord.POST("setAuthorityBtn", authorityBtnApi.SetAuthorityBtn) + authorityRouterWithoutRecord.POST("canRemoveAuthorityBtn", authorityBtnApi.CanRemoveAuthorityBtn) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_auto_code.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_auto_code.go new file mode 100644 index 000000000..e25e1cef8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_auto_code.go @@ -0,0 +1,40 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type AutoCodeRouter struct{} + +func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup, RouterPublic *gin.RouterGroup) { + autoCodeRouter := Router.Group("autoCode") + publicAutoCodeRouter := RouterPublic.Group("autoCode") + { + autoCodeRouter.GET("getDB", autoCodeApi.GetDB) // 获取数据库 + autoCodeRouter.GET("getTables", autoCodeApi.GetTables) // 获取对应数据库的表 + autoCodeRouter.GET("getColumn", autoCodeApi.GetColumn) // 获取指定表所有字段信息 + } + { + autoCodeRouter.POST("preview", autoCodeTemplateApi.Preview) // 获取自动创建代码预览 + autoCodeRouter.POST("createTemp", autoCodeTemplateApi.Create) // 创建自动化代码 + autoCodeRouter.POST("addFunc", autoCodeTemplateApi.AddFunc) // 为代码插入方法 + } + { + autoCodeRouter.POST("getPackage", autoCodePackageApi.All) // 获取package包 + autoCodeRouter.POST("delPackage", autoCodePackageApi.Delete) // 删除package包 + autoCodeRouter.POST("createPackage", autoCodePackageApi.Create) // 创建package包 + } + { + autoCodeRouter.GET("getTemplates", autoCodePackageApi.Templates) // 创建package包 + } + { + autoCodeRouter.POST("pubPlug", autoCodePluginApi.Packaged) // 打包插件 + autoCodeRouter.POST("installPlugin", autoCodePluginApi.Install) // 自动安装插件 + + } + { + publicAutoCodeRouter.POST("llmAuto", autoCodeApi.LLMAuto) + publicAutoCodeRouter.POST("initMenu", autoCodePluginApi.InitMenu) // 同步插件菜单 + publicAutoCodeRouter.POST("initAPI", autoCodePluginApi.InitAPI) // 同步插件API + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_auto_code_history.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_auto_code_history.go new file mode 100644 index 000000000..42a2bef86 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_auto_code_history.go @@ -0,0 +1,17 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type AutoCodeHistoryRouter struct{} + +func (s *AutoCodeRouter) InitAutoCodeHistoryRouter(Router *gin.RouterGroup) { + autoCodeHistoryRouter := Router.Group("autoCode") + { + autoCodeHistoryRouter.POST("getMeta", autocodeHistoryApi.First) // 根据id获取meta信息 + autoCodeHistoryRouter.POST("rollback", autocodeHistoryApi.RollBack) // 回滚 + autoCodeHistoryRouter.POST("delSysHistory", autocodeHistoryApi.Delete) // 删除回滚记录 + autoCodeHistoryRouter.POST("getSysHistory", autocodeHistoryApi.GetList) // 获取回滚记录分页 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_base.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_base.go new file mode 100644 index 000000000..7d959bbc9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_base.go @@ -0,0 +1,16 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type BaseRouter struct{} + +func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) { + baseRouter := Router.Group("base") + { + baseRouter.POST("login", baseApi.Login) + baseRouter.POST("captcha", baseApi.Captcha) + } + return baseRouter +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_casbin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_casbin.go new file mode 100644 index 000000000..e4a3eb12c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_casbin.go @@ -0,0 +1,19 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type CasbinRouter struct{} + +func (s *CasbinRouter) InitCasbinRouter(Router *gin.RouterGroup) { + casbinRouter := Router.Group("casbin").Use(middleware.OperationRecord()) + casbinRouterWithoutRecord := Router.Group("casbin") + { + casbinRouter.POST("updateCasbin", casbinApi.UpdateCasbin) + } + { + casbinRouterWithoutRecord.POST("getPolicyPathByAuthorityId", casbinApi.GetPolicyPathByAuthorityId) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_dictionary.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_dictionary.go new file mode 100644 index 000000000..41ce85ec9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_dictionary.go @@ -0,0 +1,22 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type DictionaryRouter struct{} + +func (s *DictionaryRouter) InitSysDictionaryRouter(Router *gin.RouterGroup) { + sysDictionaryRouter := Router.Group("sysDictionary").Use(middleware.OperationRecord()) + sysDictionaryRouterWithoutRecord := Router.Group("sysDictionary") + { + sysDictionaryRouter.POST("createSysDictionary", dictionaryApi.CreateSysDictionary) // 新建SysDictionary + sysDictionaryRouter.DELETE("deleteSysDictionary", dictionaryApi.DeleteSysDictionary) // 删除SysDictionary + sysDictionaryRouter.PUT("updateSysDictionary", dictionaryApi.UpdateSysDictionary) // 更新SysDictionary + } + { + sysDictionaryRouterWithoutRecord.GET("findSysDictionary", dictionaryApi.FindSysDictionary) // 根据ID获取SysDictionary + sysDictionaryRouterWithoutRecord.GET("getSysDictionaryList", dictionaryApi.GetSysDictionaryList) // 获取SysDictionary列表 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_dictionary_detail.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_dictionary_detail.go new file mode 100644 index 000000000..cde6bdcb6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_dictionary_detail.go @@ -0,0 +1,22 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type DictionaryDetailRouter struct{} + +func (s *DictionaryDetailRouter) InitSysDictionaryDetailRouter(Router *gin.RouterGroup) { + dictionaryDetailRouter := Router.Group("sysDictionaryDetail").Use(middleware.OperationRecord()) + dictionaryDetailRouterWithoutRecord := Router.Group("sysDictionaryDetail") + { + dictionaryDetailRouter.POST("createSysDictionaryDetail", dictionaryDetailApi.CreateSysDictionaryDetail) // 新建SysDictionaryDetail + dictionaryDetailRouter.DELETE("deleteSysDictionaryDetail", dictionaryDetailApi.DeleteSysDictionaryDetail) // 删除SysDictionaryDetail + dictionaryDetailRouter.PUT("updateSysDictionaryDetail", dictionaryDetailApi.UpdateSysDictionaryDetail) // 更新SysDictionaryDetail + } + { + dictionaryDetailRouterWithoutRecord.GET("findSysDictionaryDetail", dictionaryDetailApi.FindSysDictionaryDetail) // 根据ID获取SysDictionaryDetail + dictionaryDetailRouterWithoutRecord.GET("getSysDictionaryDetailList", dictionaryDetailApi.GetSysDictionaryDetailList) // 获取SysDictionaryDetail列表 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_export_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_export_template.go new file mode 100644 index 000000000..3e92a90c1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_export_template.go @@ -0,0 +1,28 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type SysExportTemplateRouter struct { +} + +// InitSysExportTemplateRouter 初始化 导出模板 路由信息 +func (s *SysExportTemplateRouter) InitSysExportTemplateRouter(Router *gin.RouterGroup) { + sysExportTemplateRouter := Router.Group("sysExportTemplate").Use(middleware.OperationRecord()) + sysExportTemplateRouterWithoutRecord := Router.Group("sysExportTemplate") + { + sysExportTemplateRouter.POST("createSysExportTemplate", exportTemplateApi.CreateSysExportTemplate) // 新建导出模板 + sysExportTemplateRouter.DELETE("deleteSysExportTemplate", exportTemplateApi.DeleteSysExportTemplate) // 删除导出模板 + sysExportTemplateRouter.DELETE("deleteSysExportTemplateByIds", exportTemplateApi.DeleteSysExportTemplateByIds) // 批量删除导出模板 + sysExportTemplateRouter.PUT("updateSysExportTemplate", exportTemplateApi.UpdateSysExportTemplate) // 更新导出模板 + sysExportTemplateRouter.POST("importExcel", exportTemplateApi.ImportExcel) // 更新导出模板 + } + { + sysExportTemplateRouterWithoutRecord.GET("findSysExportTemplate", exportTemplateApi.FindSysExportTemplate) // 根据ID获取导出模板 + sysExportTemplateRouterWithoutRecord.GET("getSysExportTemplateList", exportTemplateApi.GetSysExportTemplateList) // 获取导出模板列表 + sysExportTemplateRouterWithoutRecord.GET("exportExcel", exportTemplateApi.ExportExcel) // 导出表格 + sysExportTemplateRouterWithoutRecord.GET("exportTemplate", exportTemplateApi.ExportTemplate) // 导出表格模板 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_initdb.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_initdb.go new file mode 100644 index 000000000..3a6de5035 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_initdb.go @@ -0,0 +1,15 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type InitRouter struct{} + +func (s *InitRouter) InitInitRouter(Router *gin.RouterGroup) { + initRouter := Router.Group("init") + { + initRouter.POST("initdb", dbApi.InitDB) // 初始化数据库 + initRouter.POST("checkdb", dbApi.CheckDB) // 检测是否需要初始化数据库 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_jwt.go new file mode 100644 index 000000000..471603158 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_jwt.go @@ -0,0 +1,14 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type JwtRouter struct{} + +func (s *JwtRouter) InitJwtRouter(Router *gin.RouterGroup) { + jwtRouter := Router.Group("jwt") + { + jwtRouter.POST("jsonInBlacklist", jwtApi.JsonInBlacklist) // jwt加入黑名单 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_menu.go new file mode 100644 index 000000000..09584f4f7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_menu.go @@ -0,0 +1,27 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type MenuRouter struct{} + +func (s *MenuRouter) InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) { + menuRouter := Router.Group("menu").Use(middleware.OperationRecord()) + menuRouterWithoutRecord := Router.Group("menu") + { + menuRouter.POST("addBaseMenu", authorityMenuApi.AddBaseMenu) // 新增菜单 + menuRouter.POST("addMenuAuthority", authorityMenuApi.AddMenuAuthority) // 增加menu和角色关联关系 + menuRouter.POST("deleteBaseMenu", authorityMenuApi.DeleteBaseMenu) // 删除菜单 + menuRouter.POST("updateBaseMenu", authorityMenuApi.UpdateBaseMenu) // 更新菜单 + } + { + menuRouterWithoutRecord.POST("getMenu", authorityMenuApi.GetMenu) // 获取菜单树 + menuRouterWithoutRecord.POST("getMenuList", authorityMenuApi.GetMenuList) // 分页获取基础menu列表 + menuRouterWithoutRecord.POST("getBaseMenuTree", authorityMenuApi.GetBaseMenuTree) // 获取用户动态路由 + menuRouterWithoutRecord.POST("getMenuAuthority", authorityMenuApi.GetMenuAuthority) // 获取指定角色menu + menuRouterWithoutRecord.POST("getBaseMenuById", authorityMenuApi.GetBaseMenuById) // 根据id获取菜单 + } + return menuRouter +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_operation_record.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_operation_record.go new file mode 100644 index 000000000..11b841db7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_operation_record.go @@ -0,0 +1,19 @@ +package system + +import ( + "github.com/gin-gonic/gin" +) + +type OperationRecordRouter struct{} + +func (s *OperationRecordRouter) InitSysOperationRecordRouter(Router *gin.RouterGroup) { + operationRecordRouter := Router.Group("sysOperationRecord") + { + operationRecordRouter.POST("createSysOperationRecord", operationRecordApi.CreateSysOperationRecord) // 新建SysOperationRecord + operationRecordRouter.DELETE("deleteSysOperationRecord", operationRecordApi.DeleteSysOperationRecord) // 删除SysOperationRecord + operationRecordRouter.DELETE("deleteSysOperationRecordByIds", operationRecordApi.DeleteSysOperationRecordByIds) // 批量删除SysOperationRecord + operationRecordRouter.GET("findSysOperationRecord", operationRecordApi.FindSysOperationRecord) // 根据ID获取SysOperationRecord + operationRecordRouter.GET("getSysOperationRecordList", operationRecordApi.GetSysOperationRecordList) // 获取SysOperationRecord列表 + + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_params.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_params.go new file mode 100644 index 000000000..50dd2364e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_params.go @@ -0,0 +1,25 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type SysParamsRouter struct{} + +// InitSysParamsRouter 初始化 参数 路由信息 +func (s *SysParamsRouter) InitSysParamsRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) { + sysParamsRouter := Router.Group("sysParams").Use(middleware.OperationRecord()) + sysParamsRouterWithoutRecord := Router.Group("sysParams") + { + sysParamsRouter.POST("createSysParams", sysParamsApi.CreateSysParams) // 新建参数 + sysParamsRouter.DELETE("deleteSysParams", sysParamsApi.DeleteSysParams) // 删除参数 + sysParamsRouter.DELETE("deleteSysParamsByIds", sysParamsApi.DeleteSysParamsByIds) // 批量删除参数 + sysParamsRouter.PUT("updateSysParams", sysParamsApi.UpdateSysParams) // 更新参数 + } + { + sysParamsRouterWithoutRecord.GET("findSysParams", sysParamsApi.FindSysParams) // 根据ID获取参数 + sysParamsRouterWithoutRecord.GET("getSysParamsList", sysParamsApi.GetSysParamsList) // 获取参数列表 + sysParamsRouterWithoutRecord.GET("getSysParam", sysParamsApi.GetSysParam) // 根据Key获取参数 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_system.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_system.go new file mode 100644 index 000000000..1a9643f35 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_system.go @@ -0,0 +1,22 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type SysRouter struct{} + +func (s *SysRouter) InitSystemRouter(Router *gin.RouterGroup) { + sysRouter := Router.Group("system").Use(middleware.OperationRecord()) + sysRouterWithoutRecord := Router.Group("system") + + { + sysRouter.POST("setSystemConfig", systemApi.SetSystemConfig) // 设置配置文件内容 + sysRouter.POST("reloadSystem", systemApi.ReloadSystem) // 重启服务 + } + { + sysRouterWithoutRecord.POST("getSystemConfig", systemApi.GetSystemConfig) // 获取配置文件内容 + sysRouterWithoutRecord.POST("getServerInfo", systemApi.GetServerInfo) // 获取服务器信息 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_user.go new file mode 100644 index 000000000..6c14b8200 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/router/system/sys_user.go @@ -0,0 +1,28 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type UserRouter struct{} + +func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) { + userRouter := Router.Group("user").Use(middleware.OperationRecord()) + userRouterWithoutRecord := Router.Group("user") + { + userRouter.POST("admin_register", baseApi.Register) // 管理员注册账号 + userRouter.POST("changePassword", baseApi.ChangePassword) // 用户修改密码 + userRouter.POST("setUserAuthority", baseApi.SetUserAuthority) // 设置用户权限 + userRouter.DELETE("deleteUser", baseApi.DeleteUser) // 删除用户 + userRouter.PUT("setUserInfo", baseApi.SetUserInfo) // 设置用户信息 + userRouter.PUT("setSelfInfo", baseApi.SetSelfInfo) // 设置自身信息 + userRouter.POST("setUserAuthorities", baseApi.SetUserAuthorities) // 设置用户权限组 + userRouter.POST("resetPassword", baseApi.ResetPassword) // 设置用户权限组 + userRouter.PUT("setSelfSetting", baseApi.SetSelfSetting) // 用户界面配置 + } + { + userRouterWithoutRecord.POST("getUserList", baseApi.GetUserList) // 分页获取用户列表 + userRouterWithoutRecord.GET("getUserInfo", baseApi.GetUserInfo) // 获取自身信息 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/enter.go new file mode 100644 index 000000000..4dc990e7c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/enter.go @@ -0,0 +1,13 @@ +package service + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/service/example" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" +) + +var ServiceGroupApp = new(ServiceGroup) + +type ServiceGroup struct { + SystemServiceGroup system.ServiceGroup + ExampleServiceGroup example.ServiceGroup +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/enter.go new file mode 100644 index 000000000..c5a7ddaa2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/enter.go @@ -0,0 +1,6 @@ +package example + +type ServiceGroup struct { + CustomerService + FileUploadAndDownloadService +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_breakpoint_continue.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_breakpoint_continue.go new file mode 100644 index 000000000..d0363bb5d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_breakpoint_continue.go @@ -0,0 +1,71 @@ +package example + +import ( + "errors" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + "gorm.io/gorm" +) + +type FileUploadAndDownloadService struct{} + +var FileUploadAndDownloadServiceApp = new(FileUploadAndDownloadService) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: FindOrCreateFile +//@description: 上传文件时检测当前文件属性,如果没有文件则创建,有则返回文件的当前切片 +//@param: fileMd5 string, fileName string, chunkTotal int +//@return: file model.ExaFile, err error + +func (e *FileUploadAndDownloadService) FindOrCreateFile(fileMd5 string, fileName string, chunkTotal int) (file example.ExaFile, err error) { + var cfile example.ExaFile + cfile.FileMd5 = fileMd5 + cfile.FileName = fileName + cfile.ChunkTotal = chunkTotal + + if errors.Is(global.GVA_DB.Where("file_md5 = ? AND is_finish = ?", fileMd5, true).First(&file).Error, gorm.ErrRecordNotFound) { + err = global.GVA_DB.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).Preload("ExaFileChunk").FirstOrCreate(&file, cfile).Error + return file, err + } + cfile.IsFinish = true + cfile.FilePath = file.FilePath + err = global.GVA_DB.Create(&cfile).Error + return cfile, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateFileChunk +//@description: 创建文件切片记录 +//@param: id uint, fileChunkPath string, fileChunkNumber int +//@return: error + +func (e *FileUploadAndDownloadService) CreateFileChunk(id uint, fileChunkPath string, fileChunkNumber int) error { + var chunk example.ExaFileChunk + chunk.FileChunkPath = fileChunkPath + chunk.ExaFileID = id + chunk.FileChunkNumber = fileChunkNumber + err := global.GVA_DB.Create(&chunk).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteFileChunk +//@description: 删除文件切片记录 +//@param: fileMd5 string, fileName string, filePath string +//@return: error + +func (e *FileUploadAndDownloadService) DeleteFileChunk(fileMd5 string, filePath string) error { + var chunks []example.ExaFileChunk + var file example.ExaFile + err := global.GVA_DB.Where("file_md5 = ? ", fileMd5).First(&file). + Updates(map[string]interface{}{ + "IsFinish": true, + "file_path": filePath, + }).Error + if err != nil { + return err + } + err = global.GVA_DB.Where("exa_file_id = ?", file.ID).Delete(&chunks).Unscoped().Error + return err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_customer.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_customer.go new file mode 100644 index 000000000..cf816f5a3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_customer.go @@ -0,0 +1,87 @@ +package example + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemService "github.com/flipped-aurora/gin-vue-admin/server/service/system" +) + +type CustomerService struct{} + +var CustomerServiceApp = new(CustomerService) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateExaCustomer +//@description: 创建客户 +//@param: e model.ExaCustomer +//@return: err error + +func (exa *CustomerService) CreateExaCustomer(e example.ExaCustomer) (err error) { + err = global.GVA_DB.Create(&e).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteFileChunk +//@description: 删除客户 +//@param: e model.ExaCustomer +//@return: err error + +func (exa *CustomerService) DeleteExaCustomer(e example.ExaCustomer) (err error) { + err = global.GVA_DB.Delete(&e).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateExaCustomer +//@description: 更新客户 +//@param: e *model.ExaCustomer +//@return: err error + +func (exa *CustomerService) UpdateExaCustomer(e *example.ExaCustomer) (err error) { + err = global.GVA_DB.Save(e).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetExaCustomer +//@description: 获取客户信息 +//@param: id uint +//@return: customer model.ExaCustomer, err error + +func (exa *CustomerService) GetExaCustomer(id uint) (customer example.ExaCustomer, err error) { + err = global.GVA_DB.Where("id = ?", id).First(&customer).Error + return +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetCustomerInfoList +//@description: 分页获取客户列表 +//@param: sysUserAuthorityID string, info request.PageInfo +//@return: list interface{}, total int64, err error + +func (exa *CustomerService) GetCustomerInfoList(sysUserAuthorityID uint, info request.PageInfo) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + db := global.GVA_DB.Model(&example.ExaCustomer{}) + var a system.SysAuthority + a.AuthorityId = sysUserAuthorityID + auth, err := systemService.AuthorityServiceApp.GetAuthorityInfo(a) + if err != nil { + return + } + var dataId []uint + for _, v := range auth.DataAuthorityId { + dataId = append(dataId, v.AuthorityId) + } + var CustomerList []example.ExaCustomer + err = db.Where("sys_user_authority_id in ?", dataId).Count(&total).Error + if err != nil { + return CustomerList, total, err + } else { + err = db.Limit(limit).Offset(offset).Preload("SysUser").Where("sys_user_authority_id in ?", dataId).Find(&CustomerList).Error + } + return CustomerList, total, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_file_upload_download.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_file_upload_download.go new file mode 100644 index 000000000..cca3ec516 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/example/exa_file_upload_download.go @@ -0,0 +1,118 @@ +package example + +import ( + "errors" + "mime/multipart" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + "github.com/flipped-aurora/gin-vue-admin/server/utils/upload" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Upload +//@description: 创建文件上传记录 +//@param: file model.ExaFileUploadAndDownload +//@return: error + +func (e *FileUploadAndDownloadService) Upload(file example.ExaFileUploadAndDownload) error { + return global.GVA_DB.Create(&file).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: FindFile +//@description: 查询文件记录 +//@param: id uint +//@return: model.ExaFileUploadAndDownload, error + +func (e *FileUploadAndDownloadService) FindFile(id uint) (example.ExaFileUploadAndDownload, error) { + var file example.ExaFileUploadAndDownload + err := global.GVA_DB.Where("id = ?", id).First(&file).Error + return file, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteFile +//@description: 删除文件记录 +//@param: file model.ExaFileUploadAndDownload +//@return: err error + +func (e *FileUploadAndDownloadService) DeleteFile(file example.ExaFileUploadAndDownload) (err error) { + var fileFromDb example.ExaFileUploadAndDownload + fileFromDb, err = e.FindFile(file.ID) + if err != nil { + return + } + oss := upload.NewOss() + if err = oss.DeleteFile(fileFromDb.Key); err != nil { + return errors.New("文件删除失败") + } + err = global.GVA_DB.Where("id = ?", file.ID).Unscoped().Delete(&file).Error + return err +} + +// EditFileName 编辑文件名或者备注 +func (e *FileUploadAndDownloadService) EditFileName(file example.ExaFileUploadAndDownload) (err error) { + var fileFromDb example.ExaFileUploadAndDownload + return global.GVA_DB.Where("id = ?", file.ID).First(&fileFromDb).Update("name", file.Name).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetFileRecordInfoList +//@description: 分页获取数据 +//@param: info request.PageInfo +//@return: list interface{}, total int64, err error + +func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info request.PageInfo) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + keyword := info.Keyword + db := global.GVA_DB.Model(&example.ExaFileUploadAndDownload{}) + var fileLists []example.ExaFileUploadAndDownload + if len(keyword) > 0 { + db = db.Where("name LIKE ?", "%"+keyword+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&fileLists).Error + return fileLists, total, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UploadFile +//@description: 根据配置文件判断是文件上传到本地或者七牛云 +//@param: header *multipart.FileHeader, noSave string +//@return: file model.ExaFileUploadAndDownload, err error + +func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, noSave string) (file example.ExaFileUploadAndDownload, err error) { + oss := upload.NewOss() + filePath, key, uploadErr := oss.UploadFile(header) + if uploadErr != nil { + return file, uploadErr + } + s := strings.Split(header.Filename, ".") + f := example.ExaFileUploadAndDownload{ + Url: filePath, + Name: header.Filename, + Tag: s[len(s)-1], + Key: key, + } + if noSave == "0" { + return f, e.Upload(f) + } + return f, nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: ImportURL +//@description: 导入URL +//@param: file model.ExaFileUploadAndDownload +//@return: error + +func (e *FileUploadAndDownloadService) ImportURL(file *[]example.ExaFileUploadAndDownload) error { + return global.GVA_DB.Create(&file).Error +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_history.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_history.go new file mode 100644 index 000000000..8d1ec4ba4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_history.go @@ -0,0 +1,217 @@ +package system + +import ( + "context" + "encoding/json" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" + "github.com/pkg/errors" + "path" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "go.uber.org/zap" +) + +var AutocodeHistory = new(autoCodeHistory) + +type autoCodeHistory struct{} + +// Create 创建代码生成器历史记录 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [songzhibin97](https://github.com/songzhibin97) +func (s *autoCodeHistory) Create(ctx context.Context, info request.SysAutoHistoryCreate) error { + create := info.Create() + err := global.GVA_DB.WithContext(ctx).Create(&create).Error + if err != nil { + return errors.Wrap(err, "创建失败!") + } + return nil +} + +// First 根据id获取代码生成器历史的数据 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [songzhibin97](https://github.com/songzhibin97) +func (s *autoCodeHistory) First(ctx context.Context, info common.GetById) (string, error) { + var meta string + err := global.GVA_DB.WithContext(ctx).Model(model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Pluck("request", &meta).Error + if err != nil { + return "", errors.Wrap(err, "获取失败!") + } + return meta, nil +} + +// Repeat 检测重复 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [songzhibin97](https://github.com/songzhibin97) +func (s *autoCodeHistory) Repeat(businessDB, structName, Package string) bool { + var count int64 + global.GVA_DB.Model(&model.SysAutoCodeHistory{}).Where("business_db = ? and struct_name = ? and package = ? and flag = 0", businessDB, structName, Package).Count(&count) + return count > 0 +} + +// RollBack 回滚 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [songzhibin97](https://github.com/songzhibin97) +func (s *autoCodeHistory) RollBack(ctx context.Context, info request.SysAutoHistoryRollBack) error { + var history model.SysAutoCodeHistory + err := global.GVA_DB.Where("id = ?", info.ID).First(&history).Error + if err != nil { + return err + } + if history.ExportTemplateID != 0 { + err = global.GVA_DB.Delete(&model.SysExportTemplate{}, "id = ?", history.ExportTemplateID).Error + if err != nil { + return err + } + } + if info.DeleteApi { + ids := info.ApiIds(history) + err = ApiServiceApp.DeleteApisByIds(ids) + if err != nil { + global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err)) + } + } // 清除API表 + if info.DeleteMenu { + err = BaseMenuServiceApp.DeleteBaseMenu(int(history.MenuID)) + if err != nil { + return errors.Wrap(err, "删除菜单失败!") + } + } // 清除菜单表 + if info.DeleteTable { + err = s.DropTable(history.BusinessDB, history.Table) + if err != nil { + return errors.Wrap(err, "删除表失败!") + } + } // 删除表 + templates := make(map[string]string, len(history.Templates)) + for key, template := range history.Templates { + { + server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + keys := strings.Split(key, "/") + key = filepath.Join(keys...) + key = strings.TrimPrefix(key, server) + } // key + { + web := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot()) + server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + slices := strings.Split(template, "/") + template = filepath.Join(slices...) + ext := path.Ext(template) + switch ext { + case ".js", ".vue": + template = filepath.Join(web, template) + case ".go": + template = filepath.Join(server, template) + } + } // value + templates[key] = template + } + history.Templates = templates + for key, value := range history.Injections { + var injection ast.Ast + switch key { + case ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter: + + case ast.TypePackageApiModuleEnter, ast.TypePackageRouterModuleEnter, ast.TypePackageServiceModuleEnter: + var entity ast.PackageModuleEnter + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + case ast.TypePackageInitializeGorm: + var entity ast.PackageInitializeGorm + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + case ast.TypePackageInitializeRouter: + var entity ast.PackageInitializeRouter + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + case ast.TypePluginGen: + var entity ast.PluginGen + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + case ast.TypePluginApiEnter, ast.TypePluginRouterEnter, ast.TypePluginServiceEnter: + var entity ast.PluginEnter + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + case ast.TypePluginInitializeGorm: + var entity ast.PluginInitializeGorm + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + case ast.TypePluginInitializeRouter: + var entity ast.PluginInitializeRouter + _ = json.Unmarshal([]byte(value), &entity) + injection = &entity + } + if injection == nil { + continue + } + file, _ := injection.Parse("", nil) + if file != nil { + _ = injection.Rollback(file) + err = injection.Format("", nil, file) + if err != nil { + return err + } + fmt.Printf("[filepath:%s]回滚注入代码成功!\n", key) + } + } // 清除注入代码 + removeBasePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, "rm_file", strconv.FormatInt(int64(time.Now().Nanosecond()), 10)) + for _, value := range history.Templates { + if !filepath.IsAbs(value) { + continue + } + removePath := filepath.Join(removeBasePath, strings.TrimPrefix(value, global.GVA_CONFIG.AutoCode.Root)) + err = utils.FileMove(value, removePath) + if err != nil { + return errors.Wrapf(err, "[src:%s][dst:%s]文件移动失败!", value, removePath) + } + } // 移动文件 + err = global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Update("flag", 1).Error + if err != nil { + return errors.Wrap(err, "更新失败!") + } + return nil +} + +// Delete 删除历史数据 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [songzhibin97](https://github.com/songzhibin97) +func (s *autoCodeHistory) Delete(ctx context.Context, info common.GetById) error { + err := global.GVA_DB.WithContext(ctx).Where("id = ?", info.Uint()).Delete(&model.SysAutoCodeHistory{}).Error + if err != nil { + return errors.Wrap(err, "删除失败!") + } + return nil +} + +// GetList 获取系统历史数据 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [songzhibin97](https://github.com/songzhibin97) +func (s *autoCodeHistory) GetList(ctx context.Context, info common.PageInfo) (list []model.SysAutoCodeHistory, total int64, err error) { + var entities []model.SysAutoCodeHistory + db := global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{}) + err = db.Count(&total).Error + if err != nil { + return nil, total, err + } + err = db.Scopes(info.Paginate()).Order("updated_at desc").Find(&entities).Error + return entities, total, err +} + +// DropTable 获取指定数据库和指定数据表的所有字段名,类型值等 +// @author: [piexlmax](https://github.com/piexlmax) +func (s *autoCodeHistory) DropTable(BusinessDb, tableName string) error { + if BusinessDb != "" { + return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error + } else { + return global.GVA_DB.Exec("DROP TABLE " + tableName).Error + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_package.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_package.go new file mode 100644 index 000000000..218ff5388 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_package.go @@ -0,0 +1,584 @@ +package system + +import ( + "context" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" + "github.com/pkg/errors" + "go/token" + "gorm.io/gorm" + "os" + "path/filepath" + "strings" + "text/template" +) + +var AutoCodePackage = new(autoCodePackage) + +type autoCodePackage struct{} + +// Create 创建包信息 +// @author: [piexlmax](https://github.com/piexlmax) +// @author: [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodePackage) Create(ctx context.Context, info *request.SysAutoCodePackageCreate) error { + switch { + case info.Template == "": + return errors.New("模板不能为空!") + case info.Template == "page": + return errors.New("page为表单生成器!") + case info.PackageName == "": + return errors.New("PackageName不能为空!") + case token.IsKeyword(info.PackageName): + return errors.Errorf("%s为go的关键字!", info.PackageName) + case info.Template == "package": + if info.PackageName == "system" || info.PackageName == "example" { + return errors.New("不能使用已保留的package name") + } + default: + break + } + if !errors.Is(global.GVA_DB.Where("package_name = ? and template = ?", info.PackageName, info.Template).First(&model.SysAutoCodePackage{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在相同PackageName") + } + create := info.Create() + return global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + err := tx.Create(&create).Error + if err != nil { + return errors.Wrap(err, "创建失败!") + } + code := info.AutoCode() + _, asts, creates, err := s.templates(ctx, create, code) + if err != nil { + return err + } + for key, value := range creates { // key 为 模版绝对路径 + var files *template.Template + files, err = template.ParseFiles(key) + if err != nil { + return errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", key) + } + err = os.MkdirAll(filepath.Dir(value), os.ModePerm) + if err != nil { + return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value) + } + var file *os.File + file, err = os.Create(value) + if err != nil { + return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value) + } + err = files.Execute(file, code) + _ = file.Close() + if err != nil { + return errors.Wrapf(err, "[filepath:%s]生成失败!", value) + } + fmt.Printf("[template:%s][filepath:%s]生成成功!\n", key, value) + } + for key, value := range asts { + keys := strings.Split(key, "=>") + if len(keys) == 2 { + switch keys[1] { + case ast.TypePluginInitializeV2, ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter: + file, _ := value.Parse("", nil) + if file != nil { + err = value.Injection(file) + if err != nil { + return err + } + err = value.Format("", nil, file) + if err != nil { + return err + } + } + fmt.Printf("[type:%s]注入成功!\n", key) + } + } + } + return nil + }) +} + +// Delete 删除包记录 +// @author: [piexlmax](https://github.com/piexlmax) +// @author: [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodePackage) Delete(ctx context.Context, info common.GetById) error { + err := global.GVA_DB.WithContext(ctx).Delete(&model.SysAutoCodePackage{}, info.Uint()).Error + if err != nil { + return errors.Wrap(err, "删除失败!") + } + return nil +} + +// All 获取所有包 +// @author: [piexlmax](https://github.com/piexlmax) +// @author: [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodePackage) All(ctx context.Context) (entities []model.SysAutoCodePackage, err error) { + err = global.GVA_DB.WithContext(ctx).Find(&entities).Error + if err != nil { + return nil, errors.Wrap(err, "获取所有包失败!") + } + return entities, nil +} + +// Templates 获取所有模版文件夹 +// @author: [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodePackage) Templates(ctx context.Context) ([]string, error) { + templates := make([]string, 0) + entries, err := os.ReadDir("resource") + if err != nil { + return nil, errors.Wrap(err, "读取模版文件夹失败!") + } + for i := 0; i < len(entries); i++ { + if entries[i].IsDir() { + if entries[i].Name() == "page" { + continue + } // page 为表单生成器 + if entries[i].Name() == "function" { + continue + } // function 为函数生成器 + if entries[i].Name() == "preview" { + continue + } // preview 为预览代码生成器的代码 + templates = append(templates, entries[i].Name()) + } + } + return templates, nil +} + +func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCodePackage, info request.AutoCode) (code map[string]string, asts map[string]ast.Ast, creates map[string]string, err error) { + code = make(map[string]string) + asts = make(map[string]ast.Ast) + creates = make(map[string]string) + templateDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", entity.Template) + templateDirs, err := os.ReadDir(templateDir) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", templateDir) + } + for i := 0; i < len(templateDirs); i++ { + second := filepath.Join(templateDir, templateDirs[i].Name()) + switch templateDirs[i].Name() { + case "server": + var secondDirs []os.DirEntry + secondDirs, err = os.ReadDir(second) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second) + } + for j := 0; j < len(secondDirs); j++ { + if secondDirs[j].Name() == ".DS_Store" { + continue + } + three := filepath.Join(second, secondDirs[j].Name()) + if !secondDirs[j].IsDir() { + ext := filepath.Ext(secondDirs[j].Name()) + if ext != ".template" && ext != ".tpl" { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", three) + } + name := strings.TrimSuffix(secondDirs[j].Name(), ext) + if name == "main.go" || name == "plugin.go" { + pluginInitialize := &ast.PluginInitializeV2{ + Type: ast.TypePluginInitializeV2, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, name), + PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"), + ImportPath: fmt.Sprintf(`"%s/plugin/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + PackageName: entity.PackageName, + } + asts[pluginInitialize.PluginPath+"=>"+pluginInitialize.Type.String()] = pluginInitialize + creates[three] = pluginInitialize.Path + continue + } + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three) + } + switch secondDirs[j].Name() { + case "api", "router", "service": + var threeDirs []os.DirEntry + threeDirs, err = os.ReadDir(three) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) + } + for k := 0; k < len(threeDirs); k++ { + if threeDirs[k].Name() == ".DS_Store" { + continue + } + four := filepath.Join(three, threeDirs[k].Name()) + if threeDirs[k].IsDir() { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four) + } + ext := filepath.Ext(four) + if ext != ".template" && ext != ".tpl" { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) + } + api := strings.Index(threeDirs[k].Name(), "api") + hasEnter := strings.Index(threeDirs[k].Name(), "enter") + router := strings.Index(threeDirs[k].Name(), "router") + service := strings.Index(threeDirs[k].Name(), "service") + if router == -1 && api == -1 && service == -1 && hasEnter == -1 { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) + } + if entity.Template == "package" { + create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go") + if api != -1 { + create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, info.HumpPackageName+".go") + } + if hasEnter != -1 { + isApi := strings.Index(secondDirs[j].Name(), "api") + isRouter := strings.Index(secondDirs[j].Name(), "router") + isService := strings.Index(secondDirs[j].Name(), "service") + if isApi != -1 { + packageApiEnter := &ast.PackageEnter{ + Type: ast.TypePackageApiEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", "enter.go"), + ImportPath: fmt.Sprintf(`"%s/%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, "api", "v1", entity.PackageName), + StructName: utils.FirstUpper(entity.PackageName) + "ApiGroup", + PackageName: entity.PackageName, + PackageStructName: "ApiGroup", + } + asts[packageApiEnter.Path+"=>"+packageApiEnter.Type.String()] = packageApiEnter + packageApiModuleEnter := &ast.PackageModuleEnter{ + Type: ast.TypePackageApiModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, "enter.go"), + ImportPath: fmt.Sprintf(`"%s/service"`, global.GVA_CONFIG.AutoCode.Module), + StructName: info.StructName + "Api", + AppName: "ServiceGroupApp", + GroupName: utils.FirstUpper(entity.PackageName) + "ServiceGroup", + ModuleName: info.Abbreviation + "Service", + PackageName: "service", + ServiceName: info.StructName + "Service", + } + asts[packageApiModuleEnter.Path+"=>"+packageApiModuleEnter.Type.String()] = packageApiModuleEnter + creates[four] = packageApiModuleEnter.Path + } + if isRouter != -1 { + packageRouterEnter := &ast.PackageEnter{ + Type: ast.TypePackageRouterEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "enter.go"), + ImportPath: fmt.Sprintf(`"%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, secondDirs[j].Name(), entity.PackageName), + StructName: utils.FirstUpper(entity.PackageName), + PackageName: entity.PackageName, + PackageStructName: "RouterGroup", + } + asts[packageRouterEnter.Path+"=>"+packageRouterEnter.Type.String()] = packageRouterEnter + packageRouterModuleEnter := &ast.PackageModuleEnter{ + Type: ast.TypePackageRouterModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"), + ImportPath: fmt.Sprintf(`api "%s/api/v1"`, global.GVA_CONFIG.AutoCode.Module), + StructName: info.StructName + "Router", + AppName: "ApiGroupApp", + GroupName: utils.FirstUpper(entity.PackageName) + "ApiGroup", + ModuleName: info.Abbreviation + "Api", + PackageName: "api", + ServiceName: info.StructName + "Api", + } + creates[four] = packageRouterModuleEnter.Path + asts[packageRouterModuleEnter.Path+"=>"+packageRouterModuleEnter.Type.String()] = packageRouterModuleEnter + packageInitializeRouter := &ast.PackageInitializeRouter{ + Type: ast.TypePackageInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"), + ImportPath: fmt.Sprintf(`"%s/router"`, global.GVA_CONFIG.AutoCode.Module), + AppName: "RouterGroupApp", + GroupName: utils.FirstUpper(entity.PackageName), + ModuleName: entity.PackageName + "Router", + PackageName: "router", + FunctionName: "Init" + info.StructName + "Router", + LeftRouterGroupName: "privateGroup", + RightRouterGroupName: "publicGroup", + } + asts[packageInitializeRouter.Path+"=>"+packageInitializeRouter.Type.String()] = packageInitializeRouter + } + if isService != -1 { + path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)) + importPath := fmt.Sprintf(`"%s/service/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName) + packageServiceEnter := &ast.PackageEnter{ + Type: ast.TypePackageServiceEnter, + Path: path, + ImportPath: importPath, + StructName: utils.FirstUpper(entity.PackageName) + "ServiceGroup", + PackageName: entity.PackageName, + PackageStructName: "ServiceGroup", + } + asts[packageServiceEnter.Path+"=>"+packageServiceEnter.Type.String()] = packageServiceEnter + packageServiceModuleEnter := &ast.PackageModuleEnter{ + Type: ast.TypePackageServiceModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"), + StructName: info.StructName + "Service", + } + asts[packageServiceModuleEnter.Path+"=>"+packageServiceModuleEnter.Type.String()] = packageServiceModuleEnter + creates[four] = packageServiceModuleEnter.Path + } + continue + } + code[four] = create + continue + } + if hasEnter != -1 { + isApi := strings.Index(secondDirs[j].Name(), "api") + isRouter := strings.Index(secondDirs[j].Name(), "router") + isService := strings.Index(secondDirs[j].Name(), "service") + if isRouter != -1 { + pluginRouterEnter := &ast.PluginEnter{ + Type: ast.TypePluginRouterEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), + ImportPath: fmt.Sprintf(`"%s/plugin/%s/api"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + StructName: info.StructName, + StructCamelName: info.Abbreviation, + ModuleName: "api" + info.StructName, + GroupName: "Api", + PackageName: "api", + ServiceName: info.StructName, + } + asts[pluginRouterEnter.Path+"=>"+pluginRouterEnter.Type.String()] = pluginRouterEnter + creates[four] = pluginRouterEnter.Path + } + if isApi != -1 { + pluginApiEnter := &ast.PluginEnter{ + Type: ast.TypePluginApiEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), + ImportPath: fmt.Sprintf(`"%s/plugin/%s/service"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + StructName: info.StructName, + StructCamelName: info.Abbreviation, + ModuleName: "service" + info.StructName, + GroupName: "Service", + PackageName: "service", + ServiceName: info.StructName, + } + asts[pluginApiEnter.Path+"=>"+pluginApiEnter.Type.String()] = pluginApiEnter + creates[four] = pluginApiEnter.Path + } + if isService != -1 { + pluginServiceEnter := &ast.PluginEnter{ + Type: ast.TypePluginServiceEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), + StructName: info.StructName, + StructCamelName: info.Abbreviation, + } + asts[pluginServiceEnter.Path+"=>"+pluginServiceEnter.Type.String()] = pluginServiceEnter + creates[four] = pluginServiceEnter.Path + } + continue + } // enter.go + create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go") + code[four] = create + } + case "gen", "config", "initialize", "plugin", "response": + if entity.Template == "package" { + continue + } // package模板不需要生成gen, config, initialize + var threeDirs []os.DirEntry + threeDirs, err = os.ReadDir(three) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) + } + for k := 0; k < len(threeDirs); k++ { + if threeDirs[k].Name() == ".DS_Store" { + continue + } + four := filepath.Join(three, threeDirs[k].Name()) + if threeDirs[k].IsDir() { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four) + } + ext := filepath.Ext(four) + if ext != ".template" && ext != ".tpl" { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) + } + gen := strings.Index(threeDirs[k].Name(), "gen") + api := strings.Index(threeDirs[k].Name(), "api") + menu := strings.Index(threeDirs[k].Name(), "menu") + viper := strings.Index(threeDirs[k].Name(), "viper") + plugin := strings.Index(threeDirs[k].Name(), "plugin") + config := strings.Index(threeDirs[k].Name(), "config") + router := strings.Index(threeDirs[k].Name(), "router") + hasGorm := strings.Index(threeDirs[k].Name(), "gorm") + response := strings.Index(threeDirs[k].Name(), "response") + if gen != -1 && api != -1 && menu != -1 && viper != -1 && plugin != -1 && config != -1 && router != -1 && hasGorm != -1 && response != -1 { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) + } + if api != -1 || menu != -1 || viper != -1 || response != -1 || plugin != -1 || config != -1 { + creates[four] = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)) + } + if gen != -1 { + pluginGen := &ast.PluginGen{ + Type: ast.TypePluginGen, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), + ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + StructName: info.StructName, + PackageName: "model", + IsNew: true, + } + asts[pluginGen.Path+"=>"+pluginGen.Type.String()] = pluginGen + creates[four] = pluginGen.Path + } + if hasGorm != -1 { + pluginInitializeGorm := &ast.PluginInitializeGorm{ + Type: ast.TypePluginInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), + ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + StructName: info.StructName, + PackageName: "model", + IsNew: true, + } + asts[pluginInitializeGorm.Path+"=>"+pluginInitializeGorm.Type.String()] = pluginInitializeGorm + creates[four] = pluginInitializeGorm.Path + } + if router != -1 { + pluginInitializeRouter := &ast.PluginInitializeRouter{ + Type: ast.TypePluginInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)), + ImportPath: fmt.Sprintf(`"%s/plugin/%s/router"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + AppName: "Router", + GroupName: info.StructName, + PackageName: "router", + FunctionName: "Init", + LeftRouterGroupName: "public", + RightRouterGroupName: "private", + } + asts[pluginInitializeRouter.Path+"=>"+pluginInitializeRouter.Type.String()] = pluginInitializeRouter + creates[four] = pluginInitializeRouter.Path + } + } + case "model": + var threeDirs []os.DirEntry + threeDirs, err = os.ReadDir(three) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) + } + for k := 0; k < len(threeDirs); k++ { + if threeDirs[k].Name() == ".DS_Store" { + continue + } + four := filepath.Join(three, threeDirs[k].Name()) + if threeDirs[k].IsDir() { + var fourDirs []os.DirEntry + fourDirs, err = os.ReadDir(four) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", four) + } + for l := 0; l < len(fourDirs); l++ { + if fourDirs[l].Name() == ".DS_Store" { + continue + } + five := filepath.Join(four, fourDirs[l].Name()) + if fourDirs[l].IsDir() { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", five) + } + ext := filepath.Ext(five) + if ext != ".template" && ext != ".tpl" { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", five) + } + hasRequest := strings.Index(fourDirs[l].Name(), "request") + if hasRequest == -1 { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", five) + } + create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), threeDirs[k].Name(), info.HumpPackageName+".go") + if entity.Template == "package" { + create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, threeDirs[k].Name(), info.HumpPackageName+".go") + } + code[five] = create + } + continue + } + ext := filepath.Ext(threeDirs[k].Name()) + if ext != ".template" && ext != ".tpl" { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) + } + hasModel := strings.Index(threeDirs[k].Name(), "model") + if hasModel == -1 { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) + } + create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go") + if entity.Template == "package" { + packageInitializeGorm := &ast.PackageInitializeGorm{ + Type: ast.TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: fmt.Sprintf(`"%s/model/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName), + Business: info.BusinessDB, + StructName: info.StructName, + PackageName: entity.PackageName, + IsNew: true, + } + code[four] = packageInitializeGorm.Path + asts[packageInitializeGorm.Path+"=>"+packageInitializeGorm.Type.String()] = packageInitializeGorm + create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go") + } + code[four] = create + } + default: + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three) + } + } + case "web": + var secondDirs []os.DirEntry + secondDirs, err = os.ReadDir(second) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second) + } + for j := 0; j < len(secondDirs); j++ { + if secondDirs[j].Name() == ".DS_Store" { + continue + } + three := filepath.Join(second, secondDirs[j].Name()) + if !secondDirs[j].IsDir() { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three) + } + switch secondDirs[j].Name() { + case "api", "form", "view", "table": + var threeDirs []os.DirEntry + threeDirs, err = os.ReadDir(three) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three) + } + for k := 0; k < len(threeDirs); k++ { + if threeDirs[k].Name() == ".DS_Store" { + continue + } + four := filepath.Join(three, threeDirs[k].Name()) + if threeDirs[k].IsDir() { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four) + } + ext := filepath.Ext(four) + if ext != ".template" && ext != ".tpl" { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four) + } + api := strings.Index(threeDirs[k].Name(), "api") + form := strings.Index(threeDirs[k].Name(), "form") + view := strings.Index(threeDirs[k].Name(), "view") + table := strings.Index(threeDirs[k].Name(), "table") + if api == -1 && form == -1 && view == -1 && table == -1 { + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four) + } + if entity.Template == "package" { + if view != -1 || table != -1 { + formPath := filepath.Join(three, "form.vue"+ext) + value, ok := code[formPath] + if ok { + value = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.PackageName+"Form"+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) + code[formPath] = value + } + } + create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) + if api != -1 { + create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) + } + code[four] = create + continue + } + create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), "plugin", entity.PackageName, secondDirs[j].Name(), info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext))) + code[four] = create + } + default: + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three) + } + } + case "readme.txt.tpl", "readme.txt.template": + continue + default: + if templateDirs[i].Name() == ".DS_Store" { + continue + } + return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", second) + } + } + return code, asts, creates, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_package_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_package_test.go new file mode 100644 index 000000000..94285e979 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_package_test.go @@ -0,0 +1,105 @@ +package system + +import ( + "context" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "reflect" + "testing" +) + +func Test_autoCodePackage_Create(t *testing.T) { + type args struct { + ctx context.Context + info *request.SysAutoCodePackageCreate + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "测试 package", + args: args{ + ctx: context.Background(), + info: &request.SysAutoCodePackageCreate{ + Template: "package", + PackageName: "gva", + }, + }, + wantErr: false, + }, + { + name: "测试 plugin", + args: args{ + ctx: context.Background(), + info: &request.SysAutoCodePackageCreate{ + Template: "plugin", + PackageName: "gva", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &autoCodePackage{} + if err := a.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr { + t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_autoCodePackage_templates(t *testing.T) { + type args struct { + ctx context.Context + entity model.SysAutoCodePackage + info request.AutoCode + } + tests := []struct { + name string + args args + wantCode map[string]string + wantEnter map[string]map[string]string + wantErr bool + }{ + { + name: "测试1", + args: args{ + ctx: context.Background(), + entity: model.SysAutoCodePackage{ + Desc: "描述", + Label: "展示名", + Template: "plugin", + PackageName: "preview", + }, + info: request.AutoCode{ + Abbreviation: "user", + HumpPackageName: "user", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &autoCodePackage{} + gotCode, gotEnter, gotCreates, err := s.templates(tt.args.ctx, tt.args.entity, tt.args.info) + if (err != nil) != tt.wantErr { + t.Errorf("templates() error = %v, wantErr %v", err, tt.wantErr) + return + } + for key, value := range gotCode { + t.Logf("\n") + t.Logf(key) + t.Logf(value) + t.Logf("\n") + } + t.Log(gotCreates) + if !reflect.DeepEqual(gotEnter, tt.wantEnter) { + t.Errorf("templates() gotEnter = %v, want %v", gotEnter, tt.wantEnter) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_plugin.go new file mode 100644 index 000000000..a626d0941 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_plugin.go @@ -0,0 +1,249 @@ +package system + +import ( + "bytes" + "context" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" + "github.com/mholt/archiver/v4" + cp "github.com/otiai10/copy" + "github.com/pkg/errors" + "go.uber.org/zap" + "go/parser" + "go/printer" + "go/token" + "io" + "mime/multipart" + "os" + "path/filepath" + "strings" +) + +var AutoCodePlugin = new(autoCodePlugin) + +type autoCodePlugin struct{} + +// Install 插件安装 +func (s *autoCodePlugin) Install(file *multipart.FileHeader) (web, server int, err error) { + const GVAPLUGPINATH = "./gva-plug-temp/" + defer os.RemoveAll(GVAPLUGPINATH) + _, err = os.Stat(GVAPLUGPINATH) + if os.IsNotExist(err) { + os.Mkdir(GVAPLUGPINATH, os.ModePerm) + } + + src, err := file.Open() + if err != nil { + return -1, -1, err + } + defer src.Close() + + out, err := os.Create(GVAPLUGPINATH + file.Filename) + if err != nil { + return -1, -1, err + } + defer out.Close() + + _, err = io.Copy(out, src) + + paths, err := utils.Unzip(GVAPLUGPINATH+file.Filename, GVAPLUGPINATH) + paths = filterFile(paths) + var webIndex = -1 + var serverIndex = -1 + webPlugin := "" + serverPlugin := "" + + for i := range paths { + paths[i] = filepath.ToSlash(paths[i]) + pathArr := strings.Split(paths[i], "/") + ln := len(pathArr) + + if ln < 4 { + continue + } + if pathArr[2]+"/"+pathArr[3] == `server/plugin` && len(serverPlugin) == 0 { + serverPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3]) + } + if pathArr[2]+"/"+pathArr[3] == `web/plugin` && len(webPlugin) == 0 { + webPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3]) + } + } + if len(serverPlugin) == 0 && len(webPlugin) == 0 { + zap.L().Error("非标准插件,请按照文档自动迁移使用") + return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用") + } + + if len(serverPlugin) != 0 { + err = installation(serverPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server) + if err != nil { + return webIndex, serverIndex, err + } + } + + if len(webPlugin) != 0 { + err = installation(webPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web) + if err != nil { + return webIndex, serverIndex, err + } + } + + return 1, 1, err +} + +func installation(path string, formPath string, toPath string) error { + arr := strings.Split(filepath.ToSlash(path), "/") + ln := len(arr) + if ln < 3 { + return errors.New("arr") + } + name := arr[ln-3] + + var form = filepath.Join(global.GVA_CONFIG.AutoCode.Root, formPath, path) + var to = filepath.Join(global.GVA_CONFIG.AutoCode.Root, toPath, "plugin") + _, err := os.Stat(to + name) + if err == nil { + zap.L().Error("autoPath 已存在同名插件,请自行手动安装", zap.String("to", to)) + return errors.New(toPath + "已存在同名插件,请自行手动安装") + } + return cp.Copy(form, to, cp.Options{Skip: skipMacSpecialDocument}) +} + +func filterFile(paths []string) []string { + np := make([]string, 0, len(paths)) + for _, path := range paths { + if ok, _ := skipMacSpecialDocument(nil, path, ""); ok { + continue + } + np = append(np, path) + } + return np +} + +func skipMacSpecialDocument(_ os.FileInfo, src, _ string) (bool, error) { + if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") { + return true, nil + } + return false, nil +} + +func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) { + if plugName == "" { + return "", errors.New("插件名称不能为空") + } + + // 防止路径穿越 + plugName = filepath.Clean(plugName) + + webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName) + serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName) + // 创建一个新的zip文件 + + // 判断目录是否存在 + _, err = os.Stat(webPath) + if err != nil { + return "", errors.New("web路径不存在") + } + _, err = os.Stat(serverPath) + if err != nil { + return "", errors.New("server路径不存在") + } + + fileName := plugName + ".zip" + // 创建一个新的zip文件 + files, err := archiver.FilesFromDisk(nil, map[string]string{ + webPath: plugName + "/web/plugin/" + plugName, + serverPath: plugName + "/server/plugin/" + plugName, + }) + + // create the output file we'll write to + out, err := os.Create(fileName) + if err != nil { + return + } + defer out.Close() + + // we can use the CompressedArchive type to gzip a tarball + // (compression is not required; you could use Tar directly) + format := archiver.CompressedArchive{ + Archival: archiver.Zip{}, + } + + // create the archive + err = format.Archive(context.Background(), out, files) + if err != nil { + return + } + + return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil +} + +func (s *autoCodePlugin) InitMenu(menuInfo request.InitMenu) (err error) { + menuPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", menuInfo.PlugName, "initialize", "menu.go") + src, err := os.ReadFile(menuPath) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + arrayAst := ast.FindArray(astFile, "model", "SysBaseMenu") + var menus []system.SysBaseMenu + + parentMenu := []system.SysBaseMenu{ + { + ParentId: 0, + Path: menuInfo.PlugName + "Menu", + Name: menuInfo.PlugName + "Menu", + Hidden: false, + Component: "view/routerHolder.vue", + Sort: 0, + Meta: system.Meta{ + Title: menuInfo.ParentMenu, + Icon: "school", + }, + }, + } + + err = global.GVA_DB.Find(&menus, "id in (?)", menuInfo.Menus).Error + if err != nil { + return err + } + menus = append(parentMenu, menus...) + menuExpr := ast.CreateMenuStructAst(menus) + arrayAst.Elts = *menuExpr + + var out []byte + bf := bytes.NewBuffer(out) + printer.Fprint(bf, fileSet, astFile) + + os.WriteFile(menuPath, bf.Bytes(), 0666) + return nil +} + +func (s *autoCodePlugin) InitAPI(apiInfo request.InitApi) (err error) { + apiPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", apiInfo.PlugName, "initialize", "api.go") + src, err := os.ReadFile(apiPath) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + arrayAst := ast.FindArray(astFile, "model", "SysApi") + var apis []system.SysApi + err = global.GVA_DB.Find(&apis, "id in (?)", apiInfo.APIs).Error + if err != nil { + return err + } + apisExpr := ast.CreateApiStructAst(apis) + arrayAst.Elts = *apisExpr + + var out []byte + bf := bytes.NewBuffer(out) + printer.Fprint(bf, fileSet, astFile) + + os.WriteFile(apiPath, bf.Bytes(), 0666) + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_template.go new file mode 100644 index 000000000..542072ec7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_template.go @@ -0,0 +1,409 @@ +package system + +import ( + "context" + "encoding/json" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + model "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + utilsAst "github.com/flipped-aurora/gin-vue-admin/server/utils/ast" + "github.com/pkg/errors" + "go/ast" + "go/format" + "go/parser" + "go/token" + "gorm.io/gorm" + "os" + "path/filepath" + "strings" + "text/template" +) + +var AutoCodeTemplate = new(autoCodeTemplate) + +type autoCodeTemplate struct{} + +func (s *autoCodeTemplate) checkPackage(Pkg string, template string) (err error) { + switch template { + case "package": + apiEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", Pkg, "enter.go") + _, err = os.Stat(apiEnter) + if err != nil { + return fmt.Errorf("package结构异常,缺少api/v1/%s/enter.go", Pkg) + } + serviceEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", Pkg, "enter.go") + _, err = os.Stat(serviceEnter) + if err != nil { + return fmt.Errorf("package结构异常,缺少service/%s/enter.go", Pkg) + } + routerEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", Pkg, "enter.go") + _, err = os.Stat(routerEnter) + if err != nil { + return fmt.Errorf("package结构异常,缺少router/%s/enter.go", Pkg) + } + case "plugin": + pluginEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", Pkg, "plugin.go") + _, err = os.Stat(pluginEnter) + if err != nil { + return fmt.Errorf("plugin结构异常,缺少plugin/%s/plugin.go", Pkg) + } + } + return nil +} + +// Create 创建生成自动化代码 +func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) error { + history := info.History() + var autoPkg model.SysAutoCodePackage + err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&autoPkg).Error + if err != nil { + return errors.Wrap(err, "查询包失败!") + } + err = s.checkPackage(info.Package, autoPkg.Template) + if err != nil { + return err + } + // 增加判断: 重复创建struct + if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Package) { + return errors.New("已经创建过此数据结构,请勿重复创建!") + } + + generate, templates, injections, err := s.generate(ctx, info, autoPkg) + if err != nil { + return err + } + for key, builder := range generate { + err = os.MkdirAll(filepath.Dir(key), os.ModePerm) + if err != nil { + return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", key) + } + err = os.WriteFile(key, []byte(builder.String()), 0666) + if err != nil { + return errors.Wrapf(err, "[filepath:%s]写入文件失败!", key) + } + } + + // 自动创建api + if info.AutoCreateApiToSql && !info.OnlyTemplate { + apis := info.Apis() + err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + for _, v := range apis { + var api model.SysApi + var id uint + err := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务 + return err + } + id = v.ID + } else { + id = api.ID + } + history.ApiIDs = append(history.ApiIDs, id) + } + return nil + }) + if err != nil { + return err + } + } + + // 自动创建menu + if info.AutoCreateMenuToSql { + var entity model.SysBaseMenu + var id uint + err := global.GVA_DB.WithContext(ctx).First(&entity, "name = ?", info.Abbreviation).Error + if err == nil { + id = entity.ID + } else { + entity = info.Menu(autoPkg.Template) + if info.AutoCreateBtnAuth && !info.OnlyTemplate { + entity.MenuBtn = []model.SysBaseMenuBtn{ + {SysBaseMenuID: entity.ID, Name: "add", Desc: "新增"}, + {SysBaseMenuID: entity.ID, Name: "batchDelete", Desc: "批量删除"}, + {SysBaseMenuID: entity.ID, Name: "delete", Desc: "删除"}, + {SysBaseMenuID: entity.ID, Name: "edit", Desc: "编辑"}, + {SysBaseMenuID: entity.ID, Name: "info", Desc: "详情"}, + } + if info.HasExcel { + excelBtn := []model.SysBaseMenuBtn{ + {SysBaseMenuID: entity.ID, Name: "exportTemplate", Desc: "导出模板"}, + {SysBaseMenuID: entity.ID, Name: "exportExcel", Desc: "导出Excel"}, + {SysBaseMenuID: entity.ID, Name: "importExcel", Desc: "导入Excel"}, + } + entity.MenuBtn = append(entity.MenuBtn, excelBtn...) + } + } + err = global.GVA_DB.WithContext(ctx).Create(&entity).Error + id = entity.ID + if err != nil { + return errors.Wrap(err, "创建菜单失败!") + } + } + history.MenuID = id + } + + if info.HasExcel { + dbName := info.BusinessDB + name := info.Package + "_" + info.StructName + tableName := info.TableName + fieldsMap := make(map[string]string, len(info.Fields)) + for _, field := range info.Fields { + if field.Excel { + fieldsMap[field.ColumnName] = field.FieldDesc + } + } + templateInfo, _ := json.Marshal(fieldsMap) + sysExportTemplate := model.SysExportTemplate{ + DBName: dbName, + Name: name, + TableName: tableName, + TemplateID: name, + TemplateInfo: string(templateInfo), + } + err = SysExportTemplateServiceApp.CreateSysExportTemplate(&sysExportTemplate) + if err != nil { + return err + } + history.ExportTemplateID = sysExportTemplate.ID + } + + // 创建历史记录 + history.Templates = templates + history.Injections = make(map[string]string, len(injections)) + for key, value := range injections { + bytes, _ := json.Marshal(value) + history.Injections[key] = string(bytes) + } + err = AutocodeHistory.Create(ctx, history) + if err != nil { + return err + } + return nil +} + +// Preview 预览自动化代码 +func (s *autoCodeTemplate) Preview(ctx context.Context, info request.AutoCode) (map[string]string, error) { + var entity model.SysAutoCodePackage + err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&entity).Error + if err != nil { + return nil, errors.Wrap(err, "查询包失败!") + } + codes := make(map[string]strings.Builder) + preview := make(map[string]string) + codes, _, _, err = s.generate(ctx, info, entity) + if err != nil { + return nil, err + } + for key, writer := range codes { + if len(key) > len(global.GVA_CONFIG.AutoCode.Root) { + key, _ = filepath.Rel(global.GVA_CONFIG.AutoCode.Root, key) + } + // 获取key的后缀 取消. + suffix := filepath.Ext(key)[1:] + var builder strings.Builder + builder.WriteString("```" + suffix + "\n\n") + builder.WriteString(writer.String()) + builder.WriteString("\n\n```") + preview[key] = builder.String() + } + return preview, nil +} + +func (s *autoCodeTemplate) generate(ctx context.Context, info request.AutoCode, entity model.SysAutoCodePackage) (map[string]strings.Builder, map[string]string, map[string]utilsAst.Ast, error) { + templates, asts, _, err := AutoCodePackage.templates(ctx, entity, info) + if err != nil { + return nil, nil, nil, err + } + code := make(map[string]strings.Builder) + for key, create := range templates { + var files *template.Template + files, err = template.ParseFiles(key) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]读取模版文件失败!", key) + } + var builder strings.Builder + err = files.Execute(&builder, info) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]生成文件失败!", create) + } + code[create] = builder + } // 生成文件 + injections := make(map[string]utilsAst.Ast, len(asts)) + for key, value := range asts { + keys := strings.Split(key, "=>") + if len(keys) == 2 { + if keys[1] == utilsAst.TypePluginInitializeV2 { + continue + } + if info.OnlyTemplate { + if keys[1] == utilsAst.TypePackageInitializeGorm || keys[1] == utilsAst.TypePluginInitializeGorm { + continue + } + } + if !info.AutoMigrate { + if keys[1] == utilsAst.TypePackageInitializeGorm || keys[1] == utilsAst.TypePluginInitializeGorm { + continue + } + } + var builder strings.Builder + parse, _ := value.Parse("", &builder) + if parse != nil { + _ = value.Injection(parse) + err = value.Format("", &builder, parse) + if err != nil { + return nil, nil, nil, err + } + code[keys[0]] = builder + injections[keys[1]] = value + fmt.Println(keys[0], "注入成功!") + } + } + } + // 注入代码 + return code, templates, injections, nil +} + +func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error { + autoPkg := model.SysAutoCodePackage{} + err := global.GVA_DB.First(&autoPkg, "package_name = ?", info.Package).Error + if err != nil { + return err + } + if autoPkg.Template != "package" { + info.IsPlugin = true + } + err = s.addTemplateToFile("api.go", info) + if err != nil { + return err + } + err = s.addTemplateToFile("server.go", info) + if err != nil { + return err + } + err = s.addTemplateToFile("api.js", info) + if err != nil { + return err + } + err = s.addTemplateToAst("router", info) + return nil +} + +func (s *autoCodeTemplate) getTemplateStr(t string, info request.AutoFunc) (string, error) { + tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".tpl") + files, err := template.ParseFiles(tempPath) + if err != nil { + return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath) + } + var builder strings.Builder + err = files.Execute(&builder, info) + if err != nil { + fmt.Println(err.Error()) + return "", errors.Wrapf(err, "[filpath:%s]生成文件失败!", tempPath) + } + return builder.String(), nil +} + +func (s *autoCodeTemplate) addTemplateToAst(t string, info request.AutoFunc) error { + tPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", info.Package, info.HumpPackageName+".go") + funcName := fmt.Sprintf("Init%sRouter", info.StructName) + + routerStr := "RouterWithoutAuth" + if info.IsAuth { + routerStr = "Router" + } + + stmtStr := fmt.Sprintf("%s%s.%s(\"%s\", %sApi.%s)", info.Abbreviation, routerStr, info.Method, info.Router, info.Abbreviation, info.FuncName) + if info.IsPlugin { + tPath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "router", info.HumpPackageName+".go") + stmtStr = fmt.Sprintf("group.%s(\"%s\", api%s.%s)", info.Method, info.Router, info.StructName, info.FuncName) + funcName = "Init" + } + + src, err := os.ReadFile(tPath) + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + if err != nil { + fmt.Println(err) + } + funcDecl := utilsAst.FindFunction(astFile, funcName) + stmtNode := utilsAst.CreateStmt(stmtStr) + + if info.IsAuth { + for i := 0; i < len(funcDecl.Body.List); i++ { + st := funcDecl.Body.List[i] + // 使用类型断言来检查stmt是否是一个块语句 + if blockStmt, ok := st.(*ast.BlockStmt); ok { + // 如果是,插入代码 跳出 + blockStmt.List = append(blockStmt.List, stmtNode) + break + } + } + } else { + for i := len(funcDecl.Body.List) - 1; i >= 0; i-- { + st := funcDecl.Body.List[i] + // 使用类型断言来检查stmt是否是一个块语句 + if blockStmt, ok := st.(*ast.BlockStmt); ok { + // 如果是,插入代码 跳出 + blockStmt.List = append(blockStmt.List, stmtNode) + break + } + } + } + + // 创建一个新的文件 + f, err := os.Create(tPath) + if err != nil { + return err + } + defer f.Close() + + if err := format.Node(f, fileSet, astFile); err != nil { + return err + } + return err +} + +func (s *autoCodeTemplate) addTemplateToFile(t string, info request.AutoFunc) error { + getTemplateStr, err := s.getTemplateStr(t, info) + if err != nil { + return err + } + var target string + + switch t { + case "api.go": + target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", info.Package, info.HumpPackageName+".go") + case "server.go": + target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", info.Package, info.HumpPackageName+".go") + case "api.js": + target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "api", info.Package, info.PackageName+".js") + } + if info.IsPlugin { + switch t { + case "api.go": + target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "api", info.HumpPackageName+".go") + case "server.go": + target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "service", info.HumpPackageName+".go") + case "api.js": + target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", info.Package, "api", info.PackageName+".js") + } + } + + // 打开文件,如果不存在则返回错误 + file, err := os.OpenFile(target, os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + return err + } + defer file.Close() + + // 写入内容 + _, err = fmt.Fprintln(file, getTemplateStr) + if err != nil { + fmt.Printf("写入文件失败: %s\n", err.Error()) + return err + } + + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_template_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_template_test.go new file mode 100644 index 000000000..09d8191dd --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/auto_code_template_test.go @@ -0,0 +1,84 @@ +package system + +import ( + "context" + "encoding/json" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "reflect" + "testing" +) + +func Test_autoCodeTemplate_Create(t *testing.T) { + type args struct { + ctx context.Context + info request.AutoCode + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &autoCodeTemplate{} + if err := s.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr { + t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_autoCodeTemplate_Preview(t *testing.T) { + type args struct { + ctx context.Context + info request.AutoCode + } + tests := []struct { + name string + args args + want map[string]string + wantErr bool + }{ + { + name: "测试 package", + args: args{ + ctx: context.Background(), + info: request.AutoCode{}, + }, + wantErr: false, + }, + { + name: "测试 plugin", + args: args{ + ctx: context.Background(), + info: request.AutoCode{}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testJson := `{"structName":"SysUser","tableName":"sys_users","packageName":"sysUsers","package":"gva","abbreviation":"sysUsers","description":"sysUsers表","businessDB":"","autoCreateApiToSql":true,"autoCreateMenuToSql":true,"autoMigrate":true,"gvaModel":true,"autoCreateResource":false,"fields":[{"fieldName":"Uuid","fieldDesc":"用户UUID","fieldType":"string","dataType":"varchar","fieldJson":"uuid","primaryKey":false,"dataTypeLong":"191","columnName":"uuid","comment":"用户UUID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Username","fieldDesc":"用户登录名","fieldType":"string","dataType":"varchar","fieldJson":"username","primaryKey":false,"dataTypeLong":"191","columnName":"username","comment":"用户登录名","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Password","fieldDesc":"用户登录密码","fieldType":"string","dataType":"varchar","fieldJson":"password","primaryKey":false,"dataTypeLong":"191","columnName":"password","comment":"用户登录密码","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"NickName","fieldDesc":"用户昵称","fieldType":"string","dataType":"varchar","fieldJson":"nickName","primaryKey":false,"dataTypeLong":"191","columnName":"nick_name","comment":"用户昵称","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"SideMode","fieldDesc":"用户侧边主题","fieldType":"string","dataType":"varchar","fieldJson":"sideMode","primaryKey":false,"dataTypeLong":"191","columnName":"side_mode","comment":"用户侧边主题","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"HeaderImg","fieldDesc":"用户头像","fieldType":"string","dataType":"varchar","fieldJson":"headerImg","primaryKey":false,"dataTypeLong":"191","columnName":"header_img","comment":"用户头像","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"BaseColor","fieldDesc":"基础颜色","fieldType":"string","dataType":"varchar","fieldJson":"baseColor","primaryKey":false,"dataTypeLong":"191","columnName":"base_color","comment":"基础颜色","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"AuthorityId","fieldDesc":"用户角色ID","fieldType":"int","dataType":"bigint","fieldJson":"authorityId","primaryKey":false,"dataTypeLong":"20","columnName":"authority_id","comment":"用户角色ID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Phone","fieldDesc":"用户手机号","fieldType":"string","dataType":"varchar","fieldJson":"phone","primaryKey":false,"dataTypeLong":"191","columnName":"phone","comment":"用户手机号","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Email","fieldDesc":"用户邮箱","fieldType":"string","dataType":"varchar","fieldJson":"email","primaryKey":false,"dataTypeLong":"191","columnName":"email","comment":"用户邮箱","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Enable","fieldDesc":"用户是否被冻结 1正常 2冻结","fieldType":"int","dataType":"bigint","fieldJson":"enable","primaryKey":false,"dataTypeLong":"19","columnName":"enable","comment":"用户是否被冻结 1正常 2冻结","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}}],"humpPackageName":"sys_users"}` + err := json.Unmarshal([]byte(testJson), &tt.args.info) + if err != nil { + t.Error(err) + return + } + err = tt.args.info.Pretreatment() + if err != nil { + t.Error(err) + return + } + got, err := AutoCodeTemplate.Preview(tt.args.ctx, tt.args.info) + if (err != nil) != tt.wantErr { + t.Errorf("Preview() error = %+v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Preview() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/enter.go new file mode 100644 index 000000000..634cd0017 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/enter.go @@ -0,0 +1,24 @@ +package system + +type ServiceGroup struct { + JwtService + ApiService + MenuService + UserService + CasbinService + InitDBService + AutoCodeService + BaseMenuService + AuthorityService + DictionaryService + SystemConfigService + OperationRecordService + DictionaryDetailService + AuthorityBtnService + SysExportTemplateService + SysParamsService + AutoCodePlugin autoCodePlugin + AutoCodePackage autoCodePackage + AutoCodeHistory autoCodeHistory + AutoCodeTemplate autoCodeTemplate +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/jwt_black_list.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/jwt_black_list.go new file mode 100644 index 000000000..78ae38a7e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/jwt_black_list.go @@ -0,0 +1,84 @@ +package system + +import ( + "context" + + "go.uber.org/zap" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/utils" +) + +type JwtService struct{} + +var JwtServiceApp = new(JwtService) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: JsonInBlacklist +//@description: 拉黑jwt +//@param: jwtList model.JwtBlacklist +//@return: err error + +func (jwtService *JwtService) JsonInBlacklist(jwtList system.JwtBlacklist) (err error) { + err = global.GVA_DB.Create(&jwtList).Error + if err != nil { + return + } + global.BlackCache.SetDefault(jwtList.Jwt, struct{}{}) + return +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: IsBlacklist +//@description: 判断JWT是否在黑名单内部 +//@param: jwt string +//@return: bool + +func (jwtService *JwtService) IsBlacklist(jwt string) bool { + _, ok := global.BlackCache.Get(jwt) + return ok + // err := global.GVA_DB.Where("jwt = ?", jwt).First(&system.JwtBlacklist{}).Error + // isNotFound := errors.Is(err, gorm.ErrRecordNotFound) + // return !isNotFound +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetRedisJWT +//@description: 从redis取jwt +//@param: userName string +//@return: redisJWT string, err error + +func (jwtService *JwtService) GetRedisJWT(userName string) (redisJWT string, err error) { + redisJWT, err = global.GVA_REDIS.Get(context.Background(), userName).Result() + return redisJWT, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetRedisJWT +//@description: jwt存入redis并设置过期时间 +//@param: jwt string, userName string +//@return: err error + +func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) { + // 此处过期时间等于jwt过期时间 + dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) + if err != nil { + return err + } + timer := dr + err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err() + return err +} + +func LoadAll() { + var data []string + err := global.GVA_DB.Model(&system.JwtBlacklist{}).Select("jwt").Find(&data).Error + if err != nil { + global.GVA_LOG.Error("加载数据库jwt黑名单失败!", zap.Error(err)) + return + } + for i := 0; i < len(data); i++ { + global.BlackCache.SetDefault(data[i], struct{}{}) + } // jwt黑名单 加入 BlackCache 中 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_api.go new file mode 100644 index 000000000..92c995109 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_api.go @@ -0,0 +1,325 @@ +package system + +import ( + "errors" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "gorm.io/gorm" + "strings" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateApi +//@description: 新增基础api +//@param: api model.SysApi +//@return: err error + +type ApiService struct{} + +var ApiServiceApp = new(ApiService) + +func (apiService *ApiService) CreateApi(api system.SysApi) (err error) { + if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.SysApi{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在相同api") + } + return global.GVA_DB.Create(&api).Error +} + +func (apiService *ApiService) GetApiGroups() (groups []string, groupApiMap map[string]string, err error) { + var apis []system.SysApi + err = global.GVA_DB.Find(&apis).Error + if err != nil { + return + } + groupApiMap = make(map[string]string, 0) + for i := range apis { + pathArr := strings.Split(apis[i].Path, "/") + newGroup := true + for i2 := range groups { + if groups[i2] == apis[i].ApiGroup { + newGroup = false + } + } + if newGroup { + groups = append(groups, apis[i].ApiGroup) + } + groupApiMap[pathArr[1]] = apis[i].ApiGroup + } + return +} + +func (apiService *ApiService) SyncApi() (newApis, deleteApis, ignoreApis []system.SysApi, err error) { + newApis = make([]system.SysApi, 0) + deleteApis = make([]system.SysApi, 0) + ignoreApis = make([]system.SysApi, 0) + var apis []system.SysApi + err = global.GVA_DB.Find(&apis).Error + if err != nil { + return + } + var ignores []system.SysIgnoreApi + err = global.GVA_DB.Find(&ignores).Error + if err != nil { + return + } + + for i := range ignores { + ignoreApis = append(ignoreApis, system.SysApi{ + Path: ignores[i].Path, + Description: "", + ApiGroup: "", + Method: ignores[i].Method, + }) + } + + var cacheApis []system.SysApi + for i := range global.GVA_ROUTERS { + ignoresFlag := false + for j := range ignores { + if ignores[j].Path == global.GVA_ROUTERS[i].Path && ignores[j].Method == global.GVA_ROUTERS[i].Method { + ignoresFlag = true + } + } + if !ignoresFlag { + cacheApis = append(cacheApis, system.SysApi{ + Path: global.GVA_ROUTERS[i].Path, + Method: global.GVA_ROUTERS[i].Method, + }) + } + } + + //对比数据库中的api和内存中的api,如果数据库中的api不存在于内存中,则把api放入删除数组,如果内存中的api不存在于数据库中,则把api放入新增数组 + for i := range cacheApis { + var flag bool + // 如果存在于内存不存在于api数组中 + for j := range apis { + if cacheApis[i].Path == apis[j].Path && cacheApis[i].Method == apis[j].Method { + flag = true + } + } + if !flag { + newApis = append(newApis, system.SysApi{ + Path: cacheApis[i].Path, + Description: "", + ApiGroup: "", + Method: cacheApis[i].Method, + }) + } + } + + for i := range apis { + var flag bool + // 如果存在于api数组不存在于内存 + for j := range cacheApis { + if cacheApis[j].Path == apis[i].Path && cacheApis[j].Method == apis[i].Method { + flag = true + } + } + if !flag { + deleteApis = append(deleteApis, apis[i]) + } + } + return +} + +func (apiService *ApiService) IgnoreApi(ignoreApi system.SysIgnoreApi) (err error) { + if ignoreApi.Flag { + return global.GVA_DB.Create(&ignoreApi).Error + } + return global.GVA_DB.Unscoped().Delete(&ignoreApi, "path = ? AND method = ?", ignoreApi.Path, ignoreApi.Method).Error +} + +func (apiService *ApiService) EnterSyncApi(syncApis systemRes.SysSyncApis) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var txErr error + if syncApis.NewApis != nil && len(syncApis.NewApis) > 0 { + txErr = tx.Create(&syncApis.NewApis).Error + if txErr != nil { + return txErr + } + } + for i := range syncApis.DeleteApis { + CasbinServiceApp.ClearCasbin(1, syncApis.DeleteApis[i].Path, syncApis.DeleteApis[i].Method) + txErr = tx.Delete(&system.SysApi{}, "path = ? AND method = ?", syncApis.DeleteApis[i].Path, syncApis.DeleteApis[i].Method).Error + if txErr != nil { + return txErr + } + } + return nil + }) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteApi +//@description: 删除基础api +//@param: api model.SysApi +//@return: err error + +func (apiService *ApiService) DeleteApi(api system.SysApi) (err error) { + var entity system.SysApi + err = global.GVA_DB.First(&entity, "id = ?", api.ID).Error // 根据id查询api记录 + if errors.Is(err, gorm.ErrRecordNotFound) { // api记录不存在 + return err + } + err = global.GVA_DB.Delete(&entity).Error + if err != nil { + return err + } + CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method) + return nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetAPIInfoList +//@description: 分页获取数据, +//@param: api model.SysApi, info request.PageInfo, order string, desc bool +//@return: list interface{}, total int64, err error + +func (apiService *ApiService) GetAPIInfoList(api system.SysApi, info request.PageInfo, order string, desc bool) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + db := global.GVA_DB.Model(&system.SysApi{}) + var apiList []system.SysApi + + if api.Path != "" { + db = db.Where("path LIKE ?", "%"+api.Path+"%") + } + + if api.Description != "" { + db = db.Where("description LIKE ?", "%"+api.Description+"%") + } + + if api.Method != "" { + db = db.Where("method = ?", api.Method) + } + + if api.ApiGroup != "" { + db = db.Where("api_group = ?", api.ApiGroup) + } + + err = db.Count(&total).Error + + if err != nil { + return apiList, total, err + } + + db = db.Limit(limit).Offset(offset) + OrderStr := "id desc" + if order != "" { + orderMap := make(map[string]bool, 5) + orderMap["id"] = true + orderMap["path"] = true + orderMap["api_group"] = true + orderMap["description"] = true + orderMap["method"] = true + if !orderMap[order] { + err = fmt.Errorf("非法的排序字段: %v", order) + return apiList, total, err + } + OrderStr = order + if desc { + OrderStr = order + " desc" + } + } + err = db.Order(OrderStr).Find(&apiList).Error + return apiList, total, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetAllApis +//@description: 获取所有的api +//@return: apis []model.SysApi, err error + +func (apiService *ApiService) GetAllApis(authorityID uint) (apis []system.SysApi, err error) { + parentAuthorityID, err := AuthorityServiceApp.GetParentAuthorityID(authorityID) + if err != nil { + return nil, err + } + err = global.GVA_DB.Order("id desc").Find(&apis).Error + if parentAuthorityID == 0 || !global.GVA_CONFIG.System.UseStrictAuth { + return + } + paths := CasbinServiceApp.GetPolicyPathByAuthorityId(authorityID) + // 挑选 apis里面的path和method也在paths里面的api + var authApis []system.SysApi + for i := range apis { + for j := range paths { + if paths[j].Path == apis[i].Path && paths[j].Method == apis[i].Method { + authApis = append(authApis, apis[i]) + } + } + } + return authApis, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetApiById +//@description: 根据id获取api +//@param: id float64 +//@return: api model.SysApi, err error + +func (apiService *ApiService) GetApiById(id int) (api system.SysApi, err error) { + err = global.GVA_DB.First(&api, "id = ?", id).Error + return +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateApi +//@description: 根据id更新api +//@param: api model.SysApi +//@return: err error + +func (apiService *ApiService) UpdateApi(api system.SysApi) (err error) { + var oldA system.SysApi + err = global.GVA_DB.First(&oldA, "id = ?", api.ID).Error + if oldA.Path != api.Path || oldA.Method != api.Method { + var duplicateApi system.SysApi + if ferr := global.GVA_DB.First(&duplicateApi, "path = ? AND method = ?", api.Path, api.Method).Error; ferr != nil { + if !errors.Is(ferr, gorm.ErrRecordNotFound) { + return ferr + } + } else { + if duplicateApi.ID != api.ID { + return errors.New("存在相同api路径") + } + } + + } + if err != nil { + return err + } + + err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method) + if err != nil { + return err + } + + return global.GVA_DB.Save(&api).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteApisByIds +//@description: 删除选中API +//@param: apis []model.SysApi +//@return: err error + +func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var apis []system.SysApi + err = tx.Find(&apis, "id in ?", ids.Ids).Error + if err != nil { + return err + } + err = tx.Delete(&[]system.SysApi{}, "id in ?", ids.Ids).Error + if err != nil { + return err + } + for _, sysApi := range apis { + CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method) + } + return err + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_authority.go new file mode 100644 index 000000000..087da6a4e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_authority.go @@ -0,0 +1,327 @@ +package system + +import ( + "errors" + "strconv" + + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "gorm.io/gorm" +) + +var ErrRoleExistence = errors.New("存在相同角色id") + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateAuthority +//@description: 创建一个角色 +//@param: auth model.SysAuthority +//@return: authority system.SysAuthority, err error + +type AuthorityService struct{} + +var AuthorityServiceApp = new(AuthorityService) + +func (authorityService *AuthorityService) CreateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) { + + if err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).Error; !errors.Is(err, gorm.ErrRecordNotFound) { + return auth, ErrRoleExistence + } + + e := global.GVA_DB.Transaction(func(tx *gorm.DB) error { + + if err = tx.Create(&auth).Error; err != nil { + return err + } + + auth.SysBaseMenus = systemReq.DefaultMenu() + if err = tx.Model(&auth).Association("SysBaseMenus").Replace(&auth.SysBaseMenus); err != nil { + return err + } + casbinInfos := systemReq.DefaultCasbin() + authorityId := strconv.Itoa(int(auth.AuthorityId)) + rules := [][]string{} + for _, v := range casbinInfos { + rules = append(rules, []string{authorityId, v.Path, v.Method}) + } + return CasbinServiceApp.AddPolicies(tx, rules) + }) + + return auth, e +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CopyAuthority +//@description: 复制一个角色 +//@param: copyInfo response.SysAuthorityCopyResponse +//@return: authority system.SysAuthority, err error + +func (authorityService *AuthorityService) CopyAuthority(adminAuthorityID uint, copyInfo response.SysAuthorityCopyResponse) (authority system.SysAuthority, err error) { + var authorityBox system.SysAuthority + if !errors.Is(global.GVA_DB.Where("authority_id = ?", copyInfo.Authority.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) { + return authority, ErrRoleExistence + } + copyInfo.Authority.Children = []system.SysAuthority{} + menus, err := MenuServiceApp.GetMenuAuthority(&request.GetAuthorityId{AuthorityId: copyInfo.OldAuthorityId}) + if err != nil { + return + } + var baseMenu []system.SysBaseMenu + for _, v := range menus { + intNum := v.MenuId + v.SysBaseMenu.ID = uint(intNum) + baseMenu = append(baseMenu, v.SysBaseMenu) + } + copyInfo.Authority.SysBaseMenus = baseMenu + err = global.GVA_DB.Create(©Info.Authority).Error + if err != nil { + return + } + + var btns []system.SysAuthorityBtn + + err = global.GVA_DB.Find(&btns, "authority_id = ?", copyInfo.OldAuthorityId).Error + if err != nil { + return + } + if len(btns) > 0 { + for i := range btns { + btns[i].AuthorityId = copyInfo.Authority.AuthorityId + } + err = global.GVA_DB.Create(&btns).Error + + if err != nil { + return + } + } + paths := CasbinServiceApp.GetPolicyPathByAuthorityId(copyInfo.OldAuthorityId) + err = CasbinServiceApp.UpdateCasbin(adminAuthorityID, copyInfo.Authority.AuthorityId, paths) + if err != nil { + _ = authorityService.DeleteAuthority(©Info.Authority) + } + return copyInfo.Authority, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateAuthority +//@description: 更改一个角色 +//@param: auth model.SysAuthority +//@return: authority system.SysAuthority, err error + +func (authorityService *AuthorityService) UpdateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) { + var oldAuthority system.SysAuthority + err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&oldAuthority).Error + if err != nil { + global.GVA_LOG.Debug(err.Error()) + return system.SysAuthority{}, errors.New("查询角色数据失败") + } + err = global.GVA_DB.Model(&oldAuthority).Updates(&auth).Error + return auth, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteAuthority +//@description: 删除角色 +//@param: auth *model.SysAuthority +//@return: err error + +func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthority) error { + if errors.Is(global.GVA_DB.Debug().Preload("Users").First(&auth).Error, gorm.ErrRecordNotFound) { + return errors.New("该角色不存在") + } + if len(auth.Users) != 0 { + return errors.New("此角色有用户正在使用禁止删除") + } + if !errors.Is(global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&system.SysUser{}).Error, gorm.ErrRecordNotFound) { + return errors.New("此角色有用户正在使用禁止删除") + } + if !errors.Is(global.GVA_DB.Where("parent_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).Error, gorm.ErrRecordNotFound) { + return errors.New("此角色存在子角色不允许删除") + } + + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var err error + if err = tx.Preload("SysBaseMenus").Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(auth).Unscoped().Delete(auth).Error; err != nil { + return err + } + + if len(auth.SysBaseMenus) > 0 { + if err = tx.Model(auth).Association("SysBaseMenus").Delete(auth.SysBaseMenus); err != nil { + return err + } + // err = db.Association("SysBaseMenus").Delete(&auth) + } + if len(auth.DataAuthorityId) > 0 { + if err = tx.Model(auth).Association("DataAuthorityId").Delete(auth.DataAuthorityId); err != nil { + return err + } + } + + if err = tx.Delete(&system.SysUserAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error; err != nil { + return err + } + if err = tx.Where("authority_id = ?", auth.AuthorityId).Delete(&[]system.SysAuthorityBtn{}).Error; err != nil { + return err + } + + authorityId := strconv.Itoa(int(auth.AuthorityId)) + + if err = CasbinServiceApp.RemoveFilteredPolicy(tx, authorityId); err != nil { + return err + } + + return nil + }) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetAuthorityInfoList +//@description: 分页获取数据 +//@param: info request.PageInfo +//@return: list interface{}, total int64, err error + +func (authorityService *AuthorityService) GetAuthorityInfoList(authorityID uint) (list []system.SysAuthority, err error) { + var authority system.SysAuthority + err = global.GVA_DB.Where("authority_id = ?", authorityID).First(&authority).Error + if err != nil { + return nil, err + } + var authorities []system.SysAuthority + db := global.GVA_DB.Model(&system.SysAuthority{}) + if global.GVA_CONFIG.System.UseStrictAuth { + // 当开启了严格树形结构后 + if *authority.ParentId == 0 { + // 只有顶级角色可以修改自己的权限和以下权限 + err = db.Preload("DataAuthorityId").Where("authority_id = ?", authorityID).Find(&authorities).Error + } else { + // 非顶级角色只能修改以下权限 + err = db.Debug().Preload("DataAuthorityId").Where("parent_id = ?", authorityID).Find(&authorities).Error + } + } else { + err = db.Preload("DataAuthorityId").Where("parent_id = ?", "0").Find(&authorities).Error + } + + for k := range authorities { + err = authorityService.findChildrenAuthority(&authorities[k]) + } + return authorities, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetAuthorityInfoList +//@description: 分页获取数据 +//@param: info request.PageInfo +//@return: list interface{}, total int64, err error + +func (authorityService *AuthorityService) GetStructAuthorityList(authorityID uint) (list []uint, err error) { + var auth system.SysAuthority + _ = global.GVA_DB.First(&auth, "authority_id = ?", authorityID).Error + var authorities []system.SysAuthority + err = global.GVA_DB.Preload("DataAuthorityId").Where("parent_id = ?", authorityID).Find(&authorities).Error + if len(authorities) > 0 { + for k := range authorities { + list = append(list, authorities[k].AuthorityId) + _, err = authorityService.GetStructAuthorityList(authorities[k].AuthorityId) + } + } + if *auth.ParentId == 0 { + list = append(list, authorityID) + } + return list, err +} + +func (authorityService *AuthorityService) CheckAuthorityIDAuth(authorityID, targetID uint) (err error) { + if !global.GVA_CONFIG.System.UseStrictAuth { + return nil + } + authIDS, err := authorityService.GetStructAuthorityList(authorityID) + if err != nil { + return err + } + hasAuth := false + for _, v := range authIDS { + if v == targetID { + hasAuth = true + break + } + } + if !hasAuth { + return errors.New("您提交的角色ID不合法") + } + return nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetAuthorityInfo +//@description: 获取所有角色信息 +//@param: auth model.SysAuthority +//@return: sa system.SysAuthority, err error + +func (authorityService *AuthorityService) GetAuthorityInfo(auth system.SysAuthority) (sa system.SysAuthority, err error) { + err = global.GVA_DB.Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(&sa).Error + return sa, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetDataAuthority +//@description: 设置角色资源权限 +//@param: auth model.SysAuthority +//@return: error + +func (authorityService *AuthorityService) SetDataAuthority(adminAuthorityID uint, auth system.SysAuthority) error { + var checkIDs []uint + checkIDs = append(checkIDs, auth.AuthorityId) + for i := range auth.DataAuthorityId { + checkIDs = append(checkIDs, auth.DataAuthorityId[i].AuthorityId) + } + + for i := range checkIDs { + err := authorityService.CheckAuthorityIDAuth(adminAuthorityID, checkIDs[i]) + if err != nil { + return err + } + } + + var s system.SysAuthority + global.GVA_DB.Preload("DataAuthorityId").First(&s, "authority_id = ?", auth.AuthorityId) + err := global.GVA_DB.Model(&s).Association("DataAuthorityId").Replace(&auth.DataAuthorityId) + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetMenuAuthority +//@description: 菜单与角色绑定 +//@param: auth *model.SysAuthority +//@return: error + +func (authorityService *AuthorityService) SetMenuAuthority(auth *system.SysAuthority) error { + var s system.SysAuthority + global.GVA_DB.Preload("SysBaseMenus").First(&s, "authority_id = ?", auth.AuthorityId) + err := global.GVA_DB.Model(&s).Association("SysBaseMenus").Replace(&auth.SysBaseMenus) + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: findChildrenAuthority +//@description: 查询子角色 +//@param: authority *model.SysAuthority +//@return: err error + +func (authorityService *AuthorityService) findChildrenAuthority(authority *system.SysAuthority) (err error) { + err = global.GVA_DB.Preload("DataAuthorityId").Where("parent_id = ?", authority.AuthorityId).Find(&authority.Children).Error + if len(authority.Children) > 0 { + for k := range authority.Children { + err = authorityService.findChildrenAuthority(&authority.Children[k]) + } + } + return err +} + +func (authorityService *AuthorityService) GetParentAuthorityID(authorityID uint) (parentID uint, err error) { + var authority system.SysAuthority + err = global.GVA_DB.Where("authority_id = ?", authorityID).First(&authority).Error + return *authority.ParentId, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_authority_btn.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_authority_btn.go new file mode 100644 index 000000000..1cc8f1fc7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_authority_btn.go @@ -0,0 +1,60 @@ +package system + +import ( + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "gorm.io/gorm" +) + +type AuthorityBtnService struct{} + +var AuthorityBtnServiceApp = new(AuthorityBtnService) + +func (a *AuthorityBtnService) GetAuthorityBtn(req request.SysAuthorityBtnReq) (res response.SysAuthorityBtnRes, err error) { + var authorityBtn []system.SysAuthorityBtn + err = global.GVA_DB.Find(&authorityBtn, "authority_id = ? and sys_menu_id = ?", req.AuthorityId, req.MenuID).Error + if err != nil { + return + } + var selected []uint + for _, v := range authorityBtn { + selected = append(selected, v.SysBaseMenuBtnID) + } + res.Selected = selected + return res, err +} + +func (a *AuthorityBtnService) SetAuthorityBtn(req request.SysAuthorityBtnReq) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var authorityBtn []system.SysAuthorityBtn + err = tx.Delete(&[]system.SysAuthorityBtn{}, "authority_id = ? and sys_menu_id = ?", req.AuthorityId, req.MenuID).Error + if err != nil { + return err + } + for _, v := range req.Selected { + authorityBtn = append(authorityBtn, system.SysAuthorityBtn{ + AuthorityId: req.AuthorityId, + SysMenuID: req.MenuID, + SysBaseMenuBtnID: v, + }) + } + if len(authorityBtn) > 0 { + err = tx.Create(&authorityBtn).Error + } + if err != nil { + return err + } + return err + }) +} + +func (a *AuthorityBtnService) CanRemoveAuthorityBtn(ID string) (err error) { + fErr := global.GVA_DB.First(&system.SysAuthorityBtn{}, "sys_base_menu_btn_id = ?", ID).Error + if errors.Is(fErr, gorm.ErrRecordNotFound) { + return nil + } + return errors.New("此按钮正在被使用无法删除") +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_interface.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_interface.go new file mode 100644 index 000000000..7939f14c1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_interface.go @@ -0,0 +1,55 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" +) + +type AutoCodeService struct{} + +type Database interface { + GetDB(businessDB string) (data []response.Db, err error) + GetTables(businessDB string, dbName string) (data []response.Table, err error) + GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) +} + +func (autoCodeService *AutoCodeService) Database(businessDB string) Database { + + if businessDB == "" { + switch global.GVA_CONFIG.System.DbType { + case "mysql": + return AutoCodeMysql + case "pgsql": + return AutoCodePgsql + case "mssql": + return AutoCodeMssql + case "oracle": + return AutoCodeOracle + case "sqlite": + return AutoCodeSqlite + default: + return AutoCodeMysql + } + } else { + for _, info := range global.GVA_CONFIG.DBList { + if info.AliasName == businessDB { + switch info.Type { + case "mysql": + return AutoCodeMysql + case "mssql": + return AutoCodeMssql + case "pgsql": + return AutoCodePgsql + case "oracle": + return AutoCodeOracle + case "sqlite": + return AutoCodeSqlite + default: + return AutoCodeMysql + } + } + } + return AutoCodeMysql + } + +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_mssql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_mssql.go new file mode 100644 index 000000000..68a916054 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_mssql.go @@ -0,0 +1,83 @@ +package system + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" +) + +var AutoCodeMssql = new(autoCodeMssql) + +type autoCodeMssql struct{} + +// GetDB 获取数据库的所有数据库名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeMssql) GetDB(businessDB string) (data []response.Db, err error) { + var entities []response.Db + sql := "select name AS 'database' from sysdatabases;" + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error + } + return entities, err +} + +// GetTables 获取数据库的所有表名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeMssql) GetTables(businessDB string, dbName string) (data []response.Table, err error) { + var entities []response.Table + + sql := fmt.Sprintf(`select name as 'table_name' from %s.DBO.sysobjects where xtype='U'`, dbName) + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error + } + + return entities, err +} + +// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeMssql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { + var entities []response.Column + sql := fmt.Sprintf(` +SELECT + sc.name AS column_name, + st.name AS data_type, + sc.max_length AS data_type_long, + CASE + WHEN pk.object_id IS NOT NULL THEN 1 + ELSE 0 + END AS primary_key, + sc.column_id +FROM + %s.sys.columns sc +JOIN + sys.types st ON sc.user_type_id=st.user_type_id +LEFT JOIN + %s.sys.objects so ON so.name='%s' AND so.type='U' +LEFT JOIN + %s.sys.indexes si ON si.object_id = so.object_id AND si.is_primary_key = 1 +LEFT JOIN + %s.sys.index_columns sic ON sic.object_id = si.object_id AND sic.index_id = si.index_id AND sic.column_id = sc.column_id +LEFT JOIN + %s.sys.key_constraints pk ON pk.object_id = si.object_id +WHERE + st.is_user_defined=0 AND sc.object_id = so.object_id +ORDER BY + sc.column_id +`, dbName, dbName, tableName, dbName, dbName, dbName) + + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error + } + + return entities, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_mysql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_mysql.go new file mode 100644 index 000000000..c7f0f1bc4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_mysql.go @@ -0,0 +1,83 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" +) + +var AutoCodeMysql = new(autoCodeMysql) + +type autoCodeMysql struct{} + +// GetDB 获取数据库的所有数据库名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeMysql) GetDB(businessDB string) (data []response.Db, err error) { + var entities []response.Db + sql := "SELECT SCHEMA_NAME AS `database` FROM INFORMATION_SCHEMA.SCHEMATA;" + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error + } + return entities, err +} + +// GetTables 获取数据库的所有表名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeMysql) GetTables(businessDB string, dbName string) (data []response.Table, err error) { + var entities []response.Table + sql := `select table_name as table_name from information_schema.tables where table_schema = ?` + if businessDB == "" { + err = global.GVA_DB.Raw(sql, dbName).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql, dbName).Scan(&entities).Error + } + + return entities, err +} + +// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeMysql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { + var entities []response.Column + sql := ` + SELECT + c.COLUMN_NAME column_name, + c.DATA_TYPE data_type, + CASE c.DATA_TYPE + WHEN 'longtext' THEN c.CHARACTER_MAXIMUM_LENGTH + WHEN 'varchar' THEN c.CHARACTER_MAXIMUM_LENGTH + WHEN 'double' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE) + WHEN 'decimal' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE) + WHEN 'int' THEN c.NUMERIC_PRECISION + WHEN 'bigint' THEN c.NUMERIC_PRECISION + ELSE '' + END AS data_type_long, + c.COLUMN_COMMENT column_comment, + CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key, + c.ORDINAL_POSITION +FROM + INFORMATION_SCHEMA.COLUMNS c +LEFT JOIN + INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu +ON + c.TABLE_SCHEMA = kcu.TABLE_SCHEMA + AND c.TABLE_NAME = kcu.TABLE_NAME + AND c.COLUMN_NAME = kcu.COLUMN_NAME + AND kcu.CONSTRAINT_NAME = 'PRIMARY' +WHERE + c.TABLE_NAME = ? + AND c.TABLE_SCHEMA = ? +ORDER BY + c.ORDINAL_POSITION;` + if businessDB == "" { + err = global.GVA_DB.Raw(sql, tableName, dbName).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error + } + + return entities, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_oracle.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_oracle.go new file mode 100644 index 000000000..3cdb362e9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_oracle.go @@ -0,0 +1,72 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" +) + +var AutoCodeOracle = new(autoCodeOracle) + +type autoCodeOracle struct{} + +// GetDB 获取数据库的所有数据库名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeOracle) GetDB(businessDB string) (data []response.Db, err error) { + var entities []response.Db + sql := `SELECT lower(username) AS "database" FROM all_users` + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error + return entities, err +} + +// GetTables 获取数据库的所有表名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeOracle) GetTables(businessDB string, dbName string) (data []response.Table, err error) { + var entities []response.Table + sql := `select lower(table_name) as "table_name" from all_tables where lower(owner) = ?` + + err = global.GVA_DBList[businessDB].Raw(sql, dbName).Scan(&entities).Error + return entities, err +} + +// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (s *autoCodeOracle) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { + var entities []response.Column + sql := ` + SELECT + lower(a.COLUMN_NAME) as "column_name", + (CASE WHEN a.DATA_TYPE = 'NUMBER' AND a.DATA_SCALE=0 THEN 'int' else lower(a.DATA_TYPE) end) as "data_type", + (CASE WHEN a.DATA_TYPE = 'NUMBER' THEN a.DATA_PRECISION else a.DATA_LENGTH end) as "data_type_long", + b.COMMENTS as "column_comment", + (CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key", + a.COLUMN_ID +FROM + all_tab_columns a +JOIN + all_col_comments b ON a.OWNER = b.OWNER AND a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME +LEFT JOIN + ( + SELECT + acc.OWNER, + acc.TABLE_NAME, + acc.COLUMN_NAME + FROM + all_cons_columns acc + JOIN + all_constraints ac ON acc.OWNER = ac.OWNER AND acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME + WHERE + ac.CONSTRAINT_TYPE = 'P' + ) pk ON a.OWNER = pk.OWNER AND a.TABLE_NAME = pk.TABLE_NAME AND a.COLUMN_NAME = pk.COLUMN_NAME +WHERE + lower(a.table_name) = ? + AND lower(a.OWNER) = ? +ORDER BY + a.COLUMN_ID; +` + + err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error + return entities, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_pgsql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_pgsql.go new file mode 100644 index 000000000..fae16fb95 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_pgsql.go @@ -0,0 +1,135 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" +) + +var AutoCodePgsql = new(autoCodePgsql) + +type autoCodePgsql struct{} + +// GetDB 获取数据库的所有数据库名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (a *autoCodePgsql) GetDB(businessDB string) (data []response.Db, err error) { + var entities []response.Db + sql := `SELECT datname as database FROM pg_database WHERE datistemplate = false` + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Scan(&entities).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error + } + + return entities, err +} + +// GetTables 获取数据库的所有表名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (a *autoCodePgsql) GetTables(businessDB string, dbName string) (data []response.Table, err error) { + var entities []response.Table + sql := `select table_name as table_name from information_schema.tables where table_catalog = ? and table_schema = ?` + + db := global.GVA_DB + if businessDB != "" { + db = global.GVA_DBList[businessDB] + } + + err = db.Raw(sql, dbName, "public").Scan(&entities).Error + return entities, err +} + +// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (a *autoCodePgsql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { + // todo 数据获取不全, 待完善sql + sql := ` +SELECT + psc.COLUMN_NAME AS COLUMN_NAME, + psc.udt_name AS data_type, + CASE + psc.udt_name + WHEN 'text' THEN + concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH ) + WHEN 'varchar' THEN + concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH ) + WHEN 'smallint' THEN + concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE ) + WHEN 'decimal' THEN + concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE ) + WHEN 'integer' THEN + concat_ws ( '', '', psc.NUMERIC_PRECISION ) + WHEN 'int4' THEN + concat_ws ( '', '', psc.NUMERIC_PRECISION ) + WHEN 'int8' THEN + concat_ws ( '', '', psc.NUMERIC_PRECISION ) + WHEN 'bigint' THEN + concat_ws ( '', '', psc.NUMERIC_PRECISION ) + WHEN 'timestamp' THEN + concat_ws ( '', '', psc.datetime_precision ) + ELSE '' + END AS data_type_long, + ( + SELECT + pd.description + FROM + pg_description pd + WHERE + (pd.objoid,pd.objsubid) in ( + SELECT pa.attrelid,pa.attnum + FROM + pg_attribute pa + WHERE pa.attrelid = ( SELECT oid FROM pg_class pc WHERE + pc.relname = psc.table_name + ) + and attname = psc.column_name + ) + ) AS column_comment, + ( + SELECT + COUNT(*) + FROM + pg_constraint + WHERE + contype = 'p' + AND conrelid = ( + SELECT + oid + FROM + pg_class + WHERE + relname = psc.table_name + ) + AND conkey::int[] @> ARRAY[( + SELECT + attnum::integer + FROM + pg_attribute + WHERE + attrelid = conrelid + AND attname = psc.column_name + )] + ) > 0 AS primary_key, + psc.ordinal_position +FROM + INFORMATION_SCHEMA.COLUMNS psc +WHERE + table_catalog = ? + AND table_schema = 'public' + AND TABLE_NAME = ? +ORDER BY + psc.ordinal_position; +` + var entities []response.Column + //sql = strings.ReplaceAll(sql, "@table_catalog", dbName) + //sql = strings.ReplaceAll(sql, "@table_name", tableName) + db := global.GVA_DB + if businessDB != "" { + db = global.GVA_DBList[businessDB] + } + + err = db.Raw(sql, dbName, tableName).Scan(&entities).Error + return entities, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_sqlite.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_sqlite.go new file mode 100644 index 000000000..59bcfce70 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_auto_code_sqlite.go @@ -0,0 +1,84 @@ +package system + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/response" + "path/filepath" + "strings" +) + +var AutoCodeSqlite = new(autoCodeSqlite) + +type autoCodeSqlite struct{} + +// GetDB 获取数据库的所有数据库名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (a *autoCodeSqlite) GetDB(businessDB string) (data []response.Db, err error) { + var entities []response.Db + sql := "PRAGMA database_list;" + var databaseList []struct { + File string `gorm:"column:file"` + } + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Find(&databaseList).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Find(&databaseList).Error + } + for _, database := range databaseList { + if database.File != "" { + fileName := filepath.Base(database.File) + fileExt := filepath.Ext(fileName) + fileNameWithoutExt := strings.TrimSuffix(fileName, fileExt) + + entities = append(entities, response.Db{fileNameWithoutExt}) + } + } + // entities = append(entities, response.Db{global.GVA_CONFIG.Sqlite.Dbname}) + return entities, err +} + +// GetTables 获取数据库的所有表名 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (a *autoCodeSqlite) GetTables(businessDB string, dbName string) (data []response.Table, err error) { + var entities []response.Table + sql := `SELECT name FROM sqlite_master WHERE type='table'` + tabelNames := []string{} + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Find(&tabelNames).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Find(&tabelNames).Error + } + for _, tabelName := range tabelNames { + entities = append(entities, response.Table{tabelName}) + } + return entities, err +} + +// GetColumn 获取指定数据表的所有字段名,类型值等 +// Author [piexlmax](https://github.com/piexlmax) +// Author [SliverHorn](https://github.com/SliverHorn) +func (a *autoCodeSqlite) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) { + var entities []response.Column + sql := fmt.Sprintf("PRAGMA table_info(%s);", tableName) + var columnInfos []struct { + Name string `gorm:"column:name"` + Type string `gorm:"column:type"` + Pk int `gorm:"column:pk"` + } + if businessDB == "" { + err = global.GVA_DB.Raw(sql).Scan(&columnInfos).Error + } else { + err = global.GVA_DBList[businessDB].Raw(sql).Scan(&columnInfos).Error + } + for _, columnInfo := range columnInfos { + entities = append(entities, response.Column{ + ColumnName: columnInfo.Name, + DataType: columnInfo.Type, + PrimaryKey: columnInfo.Pk == 1, + }) + } + return entities, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_base_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_base_menu.go new file mode 100644 index 000000000..913bab2f3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_base_menu.go @@ -0,0 +1,146 @@ +package system + +import ( + "errors" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "gorm.io/gorm" +) + +type BaseMenuService struct{} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteBaseMenu +//@description: 删除基础路由 +//@param: id float64 +//@return: err error + +var BaseMenuServiceApp = new(BaseMenuService) + +func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) { + err = global.GVA_DB.First(&system.SysBaseMenu{}, "parent_id = ?", id).Error + if err == nil { + return errors.New("此菜单存在子菜单不可删除") + } + var menu system.SysBaseMenu + err = global.GVA_DB.First(&menu, id).Error + if err != nil { + return errors.New("记录不存在") + } + err = global.GVA_DB.First(&system.SysAuthority{}, "default_router = ?", menu.Name).Error + if err == nil { + return errors.New("此菜单有角色正在作为首页,不可删除") + } + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + + err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error + if err != nil { + return err + } + + err = tx.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error + if err != nil { + return err + } + + err = tx.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error + if err != nil { + return err + } + err = tx.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error + if err != nil { + return err + } + + err = tx.Delete(&system.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error + if err != nil { + return err + } + return nil + }) + +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateBaseMenu +//@description: 更新路由 +//@param: menu model.SysBaseMenu +//@return: err error + +func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu system.SysBaseMenu) (err error) { + var oldMenu system.SysBaseMenu + upDateMap := make(map[string]interface{}) + upDateMap["keep_alive"] = menu.KeepAlive + upDateMap["close_tab"] = menu.CloseTab + upDateMap["default_menu"] = menu.DefaultMenu + upDateMap["parent_id"] = menu.ParentId + upDateMap["path"] = menu.Path + upDateMap["name"] = menu.Name + upDateMap["hidden"] = menu.Hidden + upDateMap["component"] = menu.Component + upDateMap["title"] = menu.Title + upDateMap["active_name"] = menu.ActiveName + upDateMap["icon"] = menu.Icon + upDateMap["sort"] = menu.Sort + + err = global.GVA_DB.Transaction(func(tx *gorm.DB) error { + tx.Where("id = ?", menu.ID).Find(&oldMenu) + if oldMenu.Name != menu.Name { + if !errors.Is(tx.Where("id <> ? AND name = ?", menu.ID, menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { + global.GVA_LOG.Debug("存在相同name修改失败") + return errors.New("存在相同name修改失败") + } + } + txErr := tx.Unscoped().Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", menu.ID).Error + if txErr != nil { + global.GVA_LOG.Debug(txErr.Error()) + return txErr + } + txErr = tx.Unscoped().Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", menu.ID).Error + if txErr != nil { + global.GVA_LOG.Debug(txErr.Error()) + return txErr + } + if len(menu.Parameters) > 0 { + for k := range menu.Parameters { + menu.Parameters[k].SysBaseMenuID = menu.ID + } + txErr = tx.Create(&menu.Parameters).Error + if txErr != nil { + global.GVA_LOG.Debug(txErr.Error()) + return txErr + } + } + + if len(menu.MenuBtn) > 0 { + for k := range menu.MenuBtn { + menu.MenuBtn[k].SysBaseMenuID = menu.ID + } + txErr = tx.Create(&menu.MenuBtn).Error + if txErr != nil { + global.GVA_LOG.Debug(txErr.Error()) + return txErr + } + } + + txErr = tx.Model(&oldMenu).Updates(upDateMap).Error + if txErr != nil { + global.GVA_LOG.Debug(txErr.Error()) + return txErr + } + return nil + }) + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetBaseMenuById +//@description: 返回当前选中menu +//@param: id float64 +//@return: menu system.SysBaseMenu, err error + +func (baseMenuService *BaseMenuService) GetBaseMenuById(id int) (menu system.SysBaseMenu, err error) { + err = global.GVA_DB.Preload("MenuBtn").Preload("Parameters").Where("id = ?", id).First(&menu).Error + return +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_casbin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_casbin.go new file mode 100644 index 000000000..32edc579e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_casbin.go @@ -0,0 +1,221 @@ +package system + +import ( + "errors" + "strconv" + "sync" + + "gorm.io/gorm" + + "github.com/casbin/casbin/v2" + "github.com/casbin/casbin/v2/model" + gormadapter "github.com/casbin/gorm-adapter/v3" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + _ "github.com/go-sql-driver/mysql" + "go.uber.org/zap" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateCasbin +//@description: 更新casbin权限 +//@param: authorityId string, casbinInfos []request.CasbinInfo +//@return: error + +type CasbinService struct{} + +var CasbinServiceApp = new(CasbinService) + +func (casbinService *CasbinService) UpdateCasbin(adminAuthorityID, AuthorityID uint, casbinInfos []request.CasbinInfo) error { + + err := AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, AuthorityID) + if err != nil { + return err + } + + if global.GVA_CONFIG.System.UseStrictAuth { + apis, e := ApiServiceApp.GetAllApis(adminAuthorityID) + if e != nil { + return e + } + + for i := range casbinInfos { + hasApi := false + for j := range apis { + if apis[j].Path == casbinInfos[i].Path && apis[j].Method == casbinInfos[i].Method { + hasApi = true + break + } + } + if !hasApi { + return errors.New("存在api不在权限列表中") + } + } + } + + authorityId := strconv.Itoa(int(AuthorityID)) + casbinService.ClearCasbin(0, authorityId) + rules := [][]string{} + //做权限去重处理 + deduplicateMap := make(map[string]bool) + for _, v := range casbinInfos { + key := authorityId + v.Path + v.Method + if _, ok := deduplicateMap[key]; !ok { + deduplicateMap[key] = true + rules = append(rules, []string{authorityId, v.Path, v.Method}) + } + } + if len(rules) == 0 { + return nil + } // 设置空权限无需调用 AddPolicies 方法 + e := casbinService.Casbin() + success, _ := e.AddPolicies(rules) + if !success { + return errors.New("存在相同api,添加失败,请联系管理员") + } + return nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateCasbinApi +//@description: API更新随动 +//@param: oldPath string, newPath string, oldMethod string, newMethod string +//@return: error + +func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error { + err := global.GVA_DB.Model(&gormadapter.CasbinRule{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{ + "v1": newPath, + "v2": newMethod, + }).Error + e := casbinService.Casbin() + err = e.LoadPolicy() + if err != nil { + return err + } + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetPolicyPathByAuthorityId +//@description: 获取权限列表 +//@param: authorityId string +//@return: pathMaps []request.CasbinInfo + +func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []request.CasbinInfo) { + e := casbinService.Casbin() + authorityId := strconv.Itoa(int(AuthorityID)) + list, _ := e.GetFilteredPolicy(0, authorityId) + for _, v := range list { + pathMaps = append(pathMaps, request.CasbinInfo{ + Path: v[1], + Method: v[2], + }) + } + return pathMaps +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: ClearCasbin +//@description: 清除匹配的权限 +//@param: v int, p ...string +//@return: bool + +func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool { + e := casbinService.Casbin() + success, _ := e.RemoveFilteredPolicy(v, p...) + return success +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: RemoveFilteredPolicy +//@description: 使用数据库方法清理筛选的politicy 此方法需要调用FreshCasbin方法才可以在系统中即刻生效 +//@param: db *gorm.DB, authorityId string +//@return: error + +func (casbinService *CasbinService) RemoveFilteredPolicy(db *gorm.DB, authorityId string) error { + return db.Delete(&gormadapter.CasbinRule{}, "v0 = ?", authorityId).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SyncPolicy +//@description: 同步目前数据库的policy 此方法需要调用FreshCasbin方法才可以在系统中即刻生效 +//@param: db *gorm.DB, authorityId string, rules [][]string +//@return: error + +func (casbinService *CasbinService) SyncPolicy(db *gorm.DB, authorityId string, rules [][]string) error { + err := casbinService.RemoveFilteredPolicy(db, authorityId) + if err != nil { + return err + } + return casbinService.AddPolicies(db, rules) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: AddPolicies +//@description: 添加匹配的权限 +//@param: v int, p ...string +//@return: bool + +func (casbinService *CasbinService) AddPolicies(db *gorm.DB, rules [][]string) error { + var casbinRules []gormadapter.CasbinRule + for i := range rules { + casbinRules = append(casbinRules, gormadapter.CasbinRule{ + Ptype: "p", + V0: rules[i][0], + V1: rules[i][1], + V2: rules[i][2], + }) + } + return db.Create(&casbinRules).Error +} + +func (CasbinService *CasbinService) FreshCasbin() (err error) { + e := CasbinService.Casbin() + err = e.LoadPolicy() + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Casbin +//@description: 持久化到数据库 引入自定义规则 +//@return: *casbin.Enforcer + +var ( + syncedCachedEnforcer *casbin.SyncedCachedEnforcer + once sync.Once +) + +func (casbinService *CasbinService) Casbin() *casbin.SyncedCachedEnforcer { + once.Do(func() { + a, err := gormadapter.NewAdapterByDB(global.GVA_DB) + if err != nil { + zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err)) + return + } + text := ` + [request_definition] + r = sub, obj, act + + [policy_definition] + p = sub, obj, act + + [role_definition] + g = _, _ + + [policy_effect] + e = some(where (p.eft == allow)) + + [matchers] + m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act + ` + m, err := model.NewModelFromString(text) + if err != nil { + zap.L().Error("字符串加载模型失败!", zap.Error(err)) + return + } + syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a) + syncedCachedEnforcer.SetExpireTime(60 * 60) + _ = syncedCachedEnforcer.LoadPolicy() + }) + return syncedCachedEnforcer +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_dictionary.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_dictionary.go new file mode 100644 index 000000000..d540a9602 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_dictionary.go @@ -0,0 +1,112 @@ +package system + +import ( + "errors" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "gorm.io/gorm" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateSysDictionary +//@description: 创建字典数据 +//@param: sysDictionary model.SysDictionary +//@return: err error + +type DictionaryService struct{} + +var DictionaryServiceApp = new(DictionaryService) + +func (dictionaryService *DictionaryService) CreateSysDictionary(sysDictionary system.SysDictionary) (err error) { + if (!errors.Is(global.GVA_DB.First(&system.SysDictionary{}, "type = ?", sysDictionary.Type).Error, gorm.ErrRecordNotFound)) { + return errors.New("存在相同的type,不允许创建") + } + err = global.GVA_DB.Create(&sysDictionary).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteSysDictionary +//@description: 删除字典数据 +//@param: sysDictionary model.SysDictionary +//@return: err error + +func (dictionaryService *DictionaryService) DeleteSysDictionary(sysDictionary system.SysDictionary) (err error) { + err = global.GVA_DB.Where("id = ?", sysDictionary.ID).Preload("SysDictionaryDetails").First(&sysDictionary).Error + if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { + return errors.New("请不要搞事") + } + if err != nil { + return err + } + err = global.GVA_DB.Delete(&sysDictionary).Error + if err != nil { + return err + } + + if sysDictionary.SysDictionaryDetails != nil { + return global.GVA_DB.Where("sys_dictionary_id=?", sysDictionary.ID).Delete(sysDictionary.SysDictionaryDetails).Error + } + return +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateSysDictionary +//@description: 更新字典数据 +//@param: sysDictionary *model.SysDictionary +//@return: err error + +func (dictionaryService *DictionaryService) UpdateSysDictionary(sysDictionary *system.SysDictionary) (err error) { + var dict system.SysDictionary + sysDictionaryMap := map[string]interface{}{ + "Name": sysDictionary.Name, + "Type": sysDictionary.Type, + "Status": sysDictionary.Status, + "Desc": sysDictionary.Desc, + } + err = global.GVA_DB.Where("id = ?", sysDictionary.ID).First(&dict).Error + if err != nil { + global.GVA_LOG.Debug(err.Error()) + return errors.New("查询字典数据失败") + } + if dict.Type != sysDictionary.Type { + if !errors.Is(global.GVA_DB.First(&system.SysDictionary{}, "type = ?", sysDictionary.Type).Error, gorm.ErrRecordNotFound) { + return errors.New("存在相同的type,不允许创建") + } + } + err = global.GVA_DB.Model(&dict).Updates(sysDictionaryMap).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetSysDictionary +//@description: 根据id或者type获取字典单条数据 +//@param: Type string, Id uint +//@return: err error, sysDictionary model.SysDictionary + +func (dictionaryService *DictionaryService) GetSysDictionary(Type string, Id uint, status *bool) (sysDictionary system.SysDictionary, err error) { + var flag = false + if status == nil { + flag = true + } else { + flag = *status + } + err = global.GVA_DB.Where("(type = ? OR id = ?) and status = ?", Type, Id, flag).Preload("SysDictionaryDetails", func(db *gorm.DB) *gorm.DB { + return db.Where("status = ?", true).Order("sort") + }).First(&sysDictionary).Error + return +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: GetSysDictionaryInfoList +//@description: 分页获取字典列表 +//@param: info request.SysDictionarySearch +//@return: err error, list interface{}, total int64 + +func (dictionaryService *DictionaryService) GetSysDictionaryInfoList() (list interface{}, err error) { + var sysDictionarys []system.SysDictionary + err = global.GVA_DB.Find(&sysDictionarys).Error + return sysDictionarys, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_dictionary_detail.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_dictionary_detail.go new file mode 100644 index 000000000..18042c788 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_dictionary_detail.go @@ -0,0 +1,118 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateSysDictionaryDetail +//@description: 创建字典详情数据 +//@param: sysDictionaryDetail model.SysDictionaryDetail +//@return: err error + +type DictionaryDetailService struct{} + +var DictionaryDetailServiceApp = new(DictionaryDetailService) + +func (dictionaryDetailService *DictionaryDetailService) CreateSysDictionaryDetail(sysDictionaryDetail system.SysDictionaryDetail) (err error) { + err = global.GVA_DB.Create(&sysDictionaryDetail).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteSysDictionaryDetail +//@description: 删除字典详情数据 +//@param: sysDictionaryDetail model.SysDictionaryDetail +//@return: err error + +func (dictionaryDetailService *DictionaryDetailService) DeleteSysDictionaryDetail(sysDictionaryDetail system.SysDictionaryDetail) (err error) { + err = global.GVA_DB.Delete(&sysDictionaryDetail).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: UpdateSysDictionaryDetail +//@description: 更新字典详情数据 +//@param: sysDictionaryDetail *model.SysDictionaryDetail +//@return: err error + +func (dictionaryDetailService *DictionaryDetailService) UpdateSysDictionaryDetail(sysDictionaryDetail *system.SysDictionaryDetail) (err error) { + err = global.GVA_DB.Save(sysDictionaryDetail).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetSysDictionaryDetail +//@description: 根据id获取字典详情单条数据 +//@param: id uint +//@return: sysDictionaryDetail system.SysDictionaryDetail, err error + +func (dictionaryDetailService *DictionaryDetailService) GetSysDictionaryDetail(id uint) (sysDictionaryDetail system.SysDictionaryDetail, err error) { + err = global.GVA_DB.Where("id = ?", id).First(&sysDictionaryDetail).Error + return +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetSysDictionaryDetailInfoList +//@description: 分页获取字典详情列表 +//@param: info request.SysDictionaryDetailSearch +//@return: list interface{}, total int64, err error + +func (dictionaryDetailService *DictionaryDetailService) GetSysDictionaryDetailInfoList(info request.SysDictionaryDetailSearch) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := global.GVA_DB.Model(&system.SysDictionaryDetail{}) + var sysDictionaryDetails []system.SysDictionaryDetail + // 如果有条件搜索 下方会自动创建搜索语句 + if info.Label != "" { + db = db.Where("label LIKE ?", "%"+info.Label+"%") + } + if info.Value != "" { + db = db.Where("value = ?", info.Value) + } + if info.Status != nil { + db = db.Where("status = ?", info.Status) + } + if info.SysDictionaryID != 0 { + db = db.Where("sys_dictionary_id = ?", info.SysDictionaryID) + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Order("sort").Find(&sysDictionaryDetails).Error + return sysDictionaryDetails, total, err +} + +// 按照字典id获取字典全部内容的方法 +func (dictionaryDetailService *DictionaryDetailService) GetDictionaryList(dictionaryID uint) (list []system.SysDictionaryDetail, err error) { + var sysDictionaryDetails []system.SysDictionaryDetail + err = global.GVA_DB.Find(&sysDictionaryDetails, "sys_dictionary_id = ?", dictionaryID).Error + return sysDictionaryDetails, err +} + +// 按照字典type获取字典全部内容的方法 +func (dictionaryDetailService *DictionaryDetailService) GetDictionaryListByType(t string) (list []system.SysDictionaryDetail, err error) { + var sysDictionaryDetails []system.SysDictionaryDetail + db := global.GVA_DB.Model(&system.SysDictionaryDetail{}).Joins("JOIN sys_dictionaries ON sys_dictionaries.id = sys_dictionary_details.sys_dictionary_id") + err = db.Debug().Find(&sysDictionaryDetails, "type = ?", t).Error + return sysDictionaryDetails, err +} + +// 按照字典id+字典内容value获取单条字典内容 +func (dictionaryDetailService *DictionaryDetailService) GetDictionaryInfoByValue(dictionaryID uint, value string) (detail system.SysDictionaryDetail, err error) { + var sysDictionaryDetail system.SysDictionaryDetail + err = global.GVA_DB.First(&sysDictionaryDetail, "sys_dictionary_id = ? and value = ?", dictionaryID, value).Error + return sysDictionaryDetail, err +} + +// 按照字典type+字典内容value获取单条字典内容 +func (dictionaryDetailService *DictionaryDetailService) GetDictionaryInfoByTypeValue(t string, value string) (detail system.SysDictionaryDetail, err error) { + var sysDictionaryDetails system.SysDictionaryDetail + db := global.GVA_DB.Model(&system.SysDictionaryDetail{}).Joins("JOIN sys_dictionaries ON sys_dictionaries.id = sys_dictionary_details.sys_dictionary_id") + err = db.First(&sysDictionaryDetails, "sys_dictionaries.type = ? and sys_dictionary_details.value = ?", t, value).Error + return sysDictionaryDetails, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_export_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_export_template.go new file mode 100644 index 000000000..868c07e7d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_export_template.go @@ -0,0 +1,421 @@ +package system + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/xuri/excelize/v2" + "gorm.io/gorm" + "mime/multipart" + "net/url" + "strconv" + "strings" + "time" +) + +type SysExportTemplateService struct { +} + +var SysExportTemplateServiceApp = new(SysExportTemplateService) + +// CreateSysExportTemplate 创建导出模板记录 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) CreateSysExportTemplate(sysExportTemplate *system.SysExportTemplate) (err error) { + err = global.GVA_DB.Create(sysExportTemplate).Error + return err +} + +// DeleteSysExportTemplate 删除导出模板记录 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) { + err = global.GVA_DB.Delete(&sysExportTemplate).Error + return err +} + +// DeleteSysExportTemplateByIds 批量删除导出模板记录 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplateByIds(ids request.IdsReq) (err error) { + err = global.GVA_DB.Delete(&[]system.SysExportTemplate{}, "id in ?", ids.Ids).Error + return err +} + +// UpdateSysExportTemplate 更新导出模板记录 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + conditions := sysExportTemplate.Conditions + e := tx.Delete(&[]system.Condition{}, "template_id = ?", sysExportTemplate.TemplateID).Error + if e != nil { + return e + } + sysExportTemplate.Conditions = nil + + joins := sysExportTemplate.JoinTemplate + e = tx.Delete(&[]system.JoinTemplate{}, "template_id = ?", sysExportTemplate.TemplateID).Error + if e != nil { + return e + } + sysExportTemplate.JoinTemplate = nil + + e = tx.Updates(&sysExportTemplate).Error + if e != nil { + return e + } + if len(conditions) > 0 { + for i := range conditions { + conditions[i].ID = 0 + } + e = tx.Create(&conditions).Error + } + if len(joins) > 0 { + for i := range joins { + joins[i].ID = 0 + } + e = tx.Create(&joins).Error + } + return e + }) +} + +// GetSysExportTemplate 根据id获取导出模板记录 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplate(id uint) (sysExportTemplate system.SysExportTemplate, err error) { + err = global.GVA_DB.Where("id = ?", id).Preload("JoinTemplate").Preload("Conditions").First(&sysExportTemplate).Error + return +} + +// GetSysExportTemplateInfoList 分页获取导出模板记录 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateInfoList(info systemReq.SysExportTemplateSearch) (list []system.SysExportTemplate, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := global.GVA_DB.Model(&system.SysExportTemplate{}) + var sysExportTemplates []system.SysExportTemplate + // 如果有条件搜索 下方会自动创建搜索语句 + if info.StartCreatedAt != nil && info.EndCreatedAt != nil { + db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + if info.TableName != "" { + db = db.Where("table_name = ?", info.TableName) + } + if info.TemplateID != "" { + db = db.Where("template_id = ?", info.TemplateID) + } + err = db.Count(&total).Error + if err != nil { + return + } + + if limit != 0 { + db = db.Limit(limit).Offset(offset) + } + + err = db.Find(&sysExportTemplates).Error + return sysExportTemplates, total, err +} + +// ExportExcel 导出Excel +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID string, values url.Values) (file *bytes.Buffer, name string, err error) { + var template system.SysExportTemplate + err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error + if err != nil { + return nil, "", err + } + f := excelize.NewFile() + defer func() { + if err := f.Close(); err != nil { + fmt.Println(err) + } + }() + // Create a new sheet. + index, err := f.NewSheet("Sheet1") + if err != nil { + fmt.Println(err) + return + } + var templateInfoMap = make(map[string]string) + columns, err := utils.GetJSONKeys(template.TemplateInfo) + if err != nil { + return nil, "", err + } + err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) + if err != nil { + return nil, "", err + } + var tableTitle []string + var selectKeyFmt []string + for _, key := range columns { + selectKeyFmt = append(selectKeyFmt, fmt.Sprintf("`%s`", key)) + tableTitle = append(tableTitle, templateInfoMap[key]) + } + + selects := strings.Join(selectKeyFmt, ", ") + var tableMap []map[string]interface{} + db := global.GVA_DB + if template.DBName != "" { + db = global.MustGetGlobalDBByDBName(template.DBName) + } + + if len(template.JoinTemplate) > 0 { + for _, join := range template.JoinTemplate { + db = db.Joins(join.JOINS + "`" + join.Table + "`" + " ON " + join.ON) + } + } + + db = db.Select(selects).Table(template.TableName) + + if len(template.Conditions) > 0 { + for _, condition := range template.Conditions { + sql := fmt.Sprintf("%s %s ?", condition.Column, condition.Operator) + value := values.Get(condition.From) + if value != "" { + if condition.Operator == "LIKE" { + value = "%" + value + "%" + } + db = db.Where(sql, value) + } + } + } + // 通过参数传入limit + limit := values.Get("limit") + if limit != "" { + l, e := strconv.Atoi(limit) + if e == nil { + db = db.Limit(l) + } + } + // 模板的默认limit + if limit == "" && template.Limit != nil && *template.Limit != 0 { + db = db.Limit(*template.Limit) + } + + // 通过参数传入offset + offset := values.Get("offset") + if offset != "" { + o, e := strconv.Atoi(offset) + if e == nil { + db = db.Offset(o) + } + } + + // 获取当前表的所有字段 + table := template.TableName + orderColumns, err := db.Migrator().ColumnTypes(table) + if err != nil { + return nil, "", err + } + + // 创建一个 map 来存储字段名 + fields := make(map[string]bool) + + for _, column := range orderColumns { + fields[column.Name()] = true + } + + // 通过参数传入order + order := values.Get("order") + + if order == "" && template.Order != "" { + // 如果没有order入参,这里会使用模板的默认排序 + order = template.Order + } + + if order != "" { + checkOrderArr := strings.Split(order, " ") + orderStr := "" + // 检查请求的排序字段是否在字段列表中 + if _, ok := fields[checkOrderArr[0]]; !ok { + return nil, "", fmt.Errorf("order by %s is not in the fields", order) + } + orderStr = checkOrderArr[0] + if len(checkOrderArr) > 1 { + if checkOrderArr[1] != "asc" && checkOrderArr[1] != "desc" { + return nil, "", fmt.Errorf("order by %s is not secure", order) + } + orderStr = orderStr + " " + checkOrderArr[1] + } + db = db.Order(orderStr) + } + + err = db.Debug().Find(&tableMap).Error + if err != nil { + return nil, "", err + } + var rows [][]string + rows = append(rows, tableTitle) + for _, exTable := range tableMap { + var row []string + for _, column := range columns { + if len(template.JoinTemplate) > 0 { + columnAs := strings.Split(column, " as ") + if len(columnAs) > 1 { + column = strings.TrimSpace(strings.Split(column, " as ")[1]) + } else { + columnArr := strings.Split(column, ".") + if len(columnArr) > 1 { + column = strings.Split(column, ".")[1] + } + } + } + // 需要对时间类型特殊处理 + if t, ok := exTable[column].(time.Time); ok { + row = append(row, t.Format("2006-01-02 15:04:05")) + } else { + row = append(row, fmt.Sprintf("%v", exTable[column])) + } + } + rows = append(rows, row) + } + for i, row := range rows { + for j, colCell := range row { + sErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(j+1), i+1), colCell) + if sErr != nil { + return nil, "", sErr + } + } + } + f.SetActiveSheet(index) + file, err = f.WriteToBuffer() + if err != nil { + return nil, "", err + } + + return file, template.Name, nil +} + +// ExportTemplate 导出Excel模板 +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templateID string) (file *bytes.Buffer, name string, err error) { + var template system.SysExportTemplate + err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error + if err != nil { + return nil, "", err + } + f := excelize.NewFile() + defer func() { + if err := f.Close(); err != nil { + fmt.Println(err) + } + }() + // Create a new sheet. + index, err := f.NewSheet("Sheet1") + if err != nil { + fmt.Println(err) + return + } + var templateInfoMap = make(map[string]string) + + columns, err := utils.GetJSONKeys(template.TemplateInfo) + + err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) + if err != nil { + return nil, "", err + } + var tableTitle []string + for _, key := range columns { + tableTitle = append(tableTitle, templateInfoMap[key]) + } + + for i := range tableTitle { + fErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(i+1), 1), tableTitle[i]) + if fErr != nil { + return nil, "", fErr + } + } + f.SetActiveSheet(index) + file, err = f.WriteToBuffer() + if err != nil { + return nil, "", err + } + + return file, template.Name, nil +} + +// ImportExcel 导入Excel +// Author [piexlmax](https://github.com/piexlmax) +func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID string, file *multipart.FileHeader) (err error) { + var template system.SysExportTemplate + err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error + if err != nil { + return err + } + + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + f, err := excelize.OpenReader(src) + if err != nil { + return err + } + + rows, err := f.GetRows("Sheet1") + if err != nil { + return err + } + + var templateInfoMap = make(map[string]string) + err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) + if err != nil { + return err + } + + var titleKeyMap = make(map[string]string) + for key, title := range templateInfoMap { + titleKeyMap[title] = key + } + + db := global.GVA_DB + if template.DBName != "" { + db = global.MustGetGlobalDBByDBName(template.DBName) + } + + return db.Transaction(func(tx *gorm.DB) error { + excelTitle := rows[0] + values := rows[1:] + items := make([]map[string]interface{}, 0, len(values)) + for _, row := range values { + var item = make(map[string]interface{}) + for ii, value := range row { + key := titleKeyMap[excelTitle[ii]] + item[key] = value + } + + needCreated := tx.Migrator().HasColumn(template.TableName, "created_at") + needUpdated := tx.Migrator().HasColumn(template.TableName, "updated_at") + + if item["created_at"] == nil && needCreated { + item["created_at"] = time.Now() + } + if item["updated_at"] == nil && needUpdated { + item["updated_at"] = time.Now() + } + + items = append(items, item) + } + cErr := tx.Table(template.TableName).CreateInBatches(&items, 1000).Error + return cErr + }) +} + +func getColumnName(n int) string { + columnName := "" + for n > 0 { + n-- + columnName = string(rune('A'+n%26)) + columnName + n /= 26 + } + return columnName +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb.go new file mode 100644 index 000000000..eac74a09d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb.go @@ -0,0 +1,189 @@ +package system + +import ( + "context" + "database/sql" + "errors" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "gorm.io/gorm" + "sort" +) + +const ( + Mysql = "mysql" + Pgsql = "pgsql" + Sqlite = "sqlite" + Mssql = "mssql" + InitSuccess = "\n[%v] --> 初始数据成功!\n" + InitDataExist = "\n[%v] --> %v 的初始数据已存在!\n" + InitDataFailed = "\n[%v] --> %v 初始数据失败! \nerr: %+v\n" + InitDataSuccess = "\n[%v] --> %v 初始数据成功!\n" +) + +const ( + InitOrderSystem = 10 + InitOrderInternal = 1000 + InitOrderExternal = 100000 +) + +var ( + ErrMissingDBContext = errors.New("missing db in context") + ErrMissingDependentContext = errors.New("missing dependent value in context") + ErrDBTypeMismatch = errors.New("db type mismatch") +) + +// SubInitializer 提供 source/*/init() 使用的接口,每个 initializer 完成一个初始化过程 +type SubInitializer interface { + InitializerName() string // 不一定代表单独一个表,所以改成了更宽泛的语义 + MigrateTable(ctx context.Context) (next context.Context, err error) + InitializeData(ctx context.Context) (next context.Context, err error) + TableCreated(ctx context.Context) bool + DataInserted(ctx context.Context) bool +} + +// TypedDBInitHandler 执行传入的 initializer +type TypedDBInitHandler interface { + EnsureDB(ctx context.Context, conf *request.InitDB) (context.Context, error) // 建库,失败属于 fatal error,因此让它 panic + WriteConfig(ctx context.Context) error // 回写配置 + InitTables(ctx context.Context, inits initSlice) error // 建表 handler + InitData(ctx context.Context, inits initSlice) error // 建数据 handler +} + +// orderedInitializer 组合一个顺序字段,以供排序 +type orderedInitializer struct { + order int + SubInitializer +} + +// initSlice 供 initializer 排序依赖时使用 +type initSlice []*orderedInitializer + +var ( + initializers initSlice + cache map[string]*orderedInitializer +) + +// RegisterInit 注册要执行的初始化过程,会在 InitDB() 时调用 +func RegisterInit(order int, i SubInitializer) { + if initializers == nil { + initializers = initSlice{} + } + if cache == nil { + cache = map[string]*orderedInitializer{} + } + name := i.InitializerName() + if _, existed := cache[name]; existed { + panic(fmt.Sprintf("Name conflict on %s", name)) + } + ni := orderedInitializer{order, i} + initializers = append(initializers, &ni) + cache[name] = &ni +} + +/* ---- * service * ---- */ + +type InitDBService struct{} + +// InitDB 创建数据库并初始化 总入口 +func (initDBService *InitDBService) InitDB(conf request.InitDB) (err error) { + ctx := context.TODO() + ctx = context.WithValue(ctx, "adminPassword", conf.AdminPassword) + if len(initializers) == 0 { + return errors.New("无可用初始化过程,请检查初始化是否已执行完成") + } + sort.Sort(&initializers) // 保证有依赖的 initializer 排在后面执行 + // Note: 若 initializer 只有单一依赖,可以写为 B=A+1, C=A+1; 由于 BC 之间没有依赖关系,所以谁先谁后并不影响初始化 + // 若存在多个依赖,可以写为 C=A+B, D=A+B+C, E=A+1; + // C必然>A|B,因此在AB之后执行,D必然>A|B|C,因此在ABC后执行,而E只依赖A,顺序与CD无关,因此E与CD哪个先执行并不影响 + var initHandler TypedDBInitHandler + switch conf.DBType { + case "mysql": + initHandler = NewMysqlInitHandler() + ctx = context.WithValue(ctx, "dbtype", "mysql") + case "pgsql": + initHandler = NewPgsqlInitHandler() + ctx = context.WithValue(ctx, "dbtype", "pgsql") + case "sqlite": + initHandler = NewSqliteInitHandler() + ctx = context.WithValue(ctx, "dbtype", "sqlite") + case "mssql": + initHandler = NewMssqlInitHandler() + ctx = context.WithValue(ctx, "dbtype", "mssql") + default: + initHandler = NewMysqlInitHandler() + ctx = context.WithValue(ctx, "dbtype", "mysql") + } + ctx, err = initHandler.EnsureDB(ctx, &conf) + if err != nil { + return err + } + + db := ctx.Value("db").(*gorm.DB) + global.GVA_DB = db + + if err = initHandler.InitTables(ctx, initializers); err != nil { + return err + } + if err = initHandler.InitData(ctx, initializers); err != nil { + return err + } + + if err = initHandler.WriteConfig(ctx); err != nil { + return err + } + initializers = initSlice{} + cache = map[string]*orderedInitializer{} + return nil +} + +// createDatabase 创建数据库( EnsureDB() 中调用 ) +func createDatabase(dsn string, driver string, createSql string) error { + db, err := sql.Open(driver, dsn) + if err != nil { + return err + } + defer func(db *sql.DB) { + err = db.Close() + if err != nil { + fmt.Println(err) + } + }(db) + if err = db.Ping(); err != nil { + return err + } + _, err = db.Exec(createSql) + return err +} + +// createTables 创建表(默认 dbInitHandler.initTables 行为) +func createTables(ctx context.Context, inits initSlice) error { + next, cancel := context.WithCancel(ctx) + defer func(c func()) { c() }(cancel) + for _, init := range inits { + if init.TableCreated(next) { + continue + } + if n, err := init.MigrateTable(next); err != nil { + return err + } else { + next = n + } + } + return nil +} + +/* -- sortable interface -- */ + +func (a initSlice) Len() int { + return len(a) +} + +func (a initSlice) Less(i, j int) bool { + return a[i].order < a[j].order +} + +func (a initSlice) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_mssql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_mssql.go new file mode 100644 index 000000000..eeeeb514f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_mssql.go @@ -0,0 +1,92 @@ +package system + +import ( + "context" + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gofrs/uuid/v5" + "github.com/gookit/color" + "gorm.io/driver/sqlserver" + "gorm.io/gorm" + "path/filepath" +) + +type MssqlInitHandler struct{} + +func NewMssqlInitHandler() *MssqlInitHandler { + return &MssqlInitHandler{} +} + +// WriteConfig mssql回写配置 +func (h MssqlInitHandler) WriteConfig(ctx context.Context) error { + c, ok := ctx.Value("config").(config.Mssql) + if !ok { + return errors.New("mssql config invalid") + } + global.GVA_CONFIG.System.DbType = "mssql" + global.GVA_CONFIG.Mssql = c + global.GVA_CONFIG.JWT.SigningKey = uuid.Must(uuid.NewV4()).String() + cs := utils.StructToMap(global.GVA_CONFIG) + for k, v := range cs { + global.GVA_VP.Set(k, v) + } + global.GVA_ACTIVE_DBNAME = &c.Dbname + return global.GVA_VP.WriteConfig() +} + +// EnsureDB 创建数据库并初始化 mssql +func (h MssqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) { + if s, ok := ctx.Value("dbtype").(string); !ok || s != "mssql" { + return ctx, ErrDBTypeMismatch + } + + c := conf.ToMssqlConfig() + next = context.WithValue(ctx, "config", c) + if c.Dbname == "" { + return ctx, nil + } // 如果没有数据库名, 则跳出初始化数据 + + dsn := conf.MssqlEmptyDsn() + + mssqlConfig := sqlserver.Config{ + DSN: dsn, // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + } + + var db *gorm.DB + + if db, err = gorm.Open(sqlserver.New(mssqlConfig), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { + return nil, err + } + + global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") + next = context.WithValue(next, "db", db) + return next, err +} + +func (h MssqlInitHandler) InitTables(ctx context.Context, inits initSlice) error { + return createTables(ctx, inits) +} + +func (h MssqlInitHandler) InitData(ctx context.Context, inits initSlice) error { + next, cancel := context.WithCancel(ctx) + defer func(c func()) { c() }(cancel) + for _, init := range inits { + if init.DataInserted(next) { + color.Info.Printf(InitDataExist, Mssql, init.InitializerName()) + continue + } + if n, err := init.InitializeData(next); err != nil { + color.Info.Printf(InitDataFailed, Mssql, init.InitializerName(), err) + return err + } else { + next = n + color.Info.Printf(InitDataSuccess, Mssql, init.InitializerName()) + } + } + color.Info.Printf(InitSuccess, Mssql) + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_mysql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_mysql.go new file mode 100644 index 000000000..62575d385 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_mysql.go @@ -0,0 +1,97 @@ +package system + +import ( + "context" + "errors" + "fmt" + "path/filepath" + + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/gookit/color" + + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gofrs/uuid/v5" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +type MysqlInitHandler struct{} + +func NewMysqlInitHandler() *MysqlInitHandler { + return &MysqlInitHandler{} +} + +// WriteConfig mysql回写配置 +func (h MysqlInitHandler) WriteConfig(ctx context.Context) error { + c, ok := ctx.Value("config").(config.Mysql) + if !ok { + return errors.New("mysql config invalid") + } + global.GVA_CONFIG.System.DbType = "mysql" + global.GVA_CONFIG.Mysql = c + global.GVA_CONFIG.JWT.SigningKey = uuid.Must(uuid.NewV4()).String() + cs := utils.StructToMap(global.GVA_CONFIG) + for k, v := range cs { + global.GVA_VP.Set(k, v) + } + global.GVA_ACTIVE_DBNAME = &c.Dbname + return global.GVA_VP.WriteConfig() +} + +// EnsureDB 创建数据库并初始化 mysql +func (h MysqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) { + if s, ok := ctx.Value("dbtype").(string); !ok || s != "mysql" { + return ctx, ErrDBTypeMismatch + } + + c := conf.ToMysqlConfig() + next = context.WithValue(ctx, "config", c) + if c.Dbname == "" { + return ctx, nil + } // 如果没有数据库名, 则跳出初始化数据 + + dsn := conf.MysqlEmptyDsn() + createSql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", c.Dbname) + if err = createDatabase(dsn, "mysql", createSql); err != nil { + return nil, err + } // 创建数据库 + + var db *gorm.DB + if db, err = gorm.Open(mysql.New(mysql.Config{ + DSN: c.Dsn(), // DSN data source name + DefaultStringSize: 191, // string 类型字段的默认长度 + SkipInitializeWithVersion: true, // 根据版本自动配置 + }), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { + return ctx, err + } + global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") + next = context.WithValue(next, "db", db) + return next, err +} + +func (h MysqlInitHandler) InitTables(ctx context.Context, inits initSlice) error { + return createTables(ctx, inits) +} + +func (h MysqlInitHandler) InitData(ctx context.Context, inits initSlice) error { + next, cancel := context.WithCancel(ctx) + defer func(c func()) { c() }(cancel) + for _, init := range inits { + if init.DataInserted(next) { + color.Info.Printf(InitDataExist, Mysql, init.InitializerName()) + continue + } + if n, err := init.InitializeData(next); err != nil { + color.Info.Printf(InitDataFailed, Mysql, init.InitializerName(), err) + return err + } else { + next = n + color.Info.Printf(InitDataSuccess, Mysql, init.InitializerName()) + } + } + color.Info.Printf(InitSuccess, Mysql) + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_pgsql.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_pgsql.go new file mode 100644 index 000000000..f1a482f0c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_pgsql.go @@ -0,0 +1,96 @@ +package system + +import ( + "context" + "errors" + "fmt" + "path/filepath" + + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/gookit/color" + + "github.com/flipped-aurora/gin-vue-admin/server/utils" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gofrs/uuid/v5" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +type PgsqlInitHandler struct{} + +func NewPgsqlInitHandler() *PgsqlInitHandler { + return &PgsqlInitHandler{} +} + +// WriteConfig pgsql 回写配置 +func (h PgsqlInitHandler) WriteConfig(ctx context.Context) error { + c, ok := ctx.Value("config").(config.Pgsql) + if !ok { + return errors.New("postgresql config invalid") + } + global.GVA_CONFIG.System.DbType = "pgsql" + global.GVA_CONFIG.Pgsql = c + global.GVA_CONFIG.JWT.SigningKey = uuid.Must(uuid.NewV4()).String() + cs := utils.StructToMap(global.GVA_CONFIG) + for k, v := range cs { + global.GVA_VP.Set(k, v) + } + global.GVA_ACTIVE_DBNAME = &c.Dbname + return global.GVA_VP.WriteConfig() +} + +// EnsureDB 创建数据库并初始化 pg +func (h PgsqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) { + if s, ok := ctx.Value("dbtype").(string); !ok || s != "pgsql" { + return ctx, ErrDBTypeMismatch + } + + c := conf.ToPgsqlConfig() + next = context.WithValue(ctx, "config", c) + if c.Dbname == "" { + return ctx, nil + } // 如果没有数据库名, 则跳出初始化数据 + + dsn := conf.PgsqlEmptyDsn() + createSql := fmt.Sprintf("CREATE DATABASE %s;", c.Dbname) + if err = createDatabase(dsn, "pgx", createSql); err != nil { + return nil, err + } // 创建数据库 + + var db *gorm.DB + if db, err = gorm.Open(postgres.New(postgres.Config{ + DSN: c.Dsn(), // DSN data source name + PreferSimpleProtocol: false, + }), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { + return ctx, err + } + global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") + next = context.WithValue(next, "db", db) + return next, err +} + +func (h PgsqlInitHandler) InitTables(ctx context.Context, inits initSlice) error { + return createTables(ctx, inits) +} + +func (h PgsqlInitHandler) InitData(ctx context.Context, inits initSlice) error { + next, cancel := context.WithCancel(ctx) + defer func(c func()) { c() }(cancel) + for i := 0; i < len(inits); i++ { + if inits[i].DataInserted(next) { + color.Info.Printf(InitDataExist, Pgsql, inits[i].InitializerName()) + continue + } + if n, err := inits[i].InitializeData(next); err != nil { + color.Info.Printf(InitDataFailed, Pgsql, inits[i].InitializerName(), err) + return err + } else { + next = n + color.Info.Printf(InitDataSuccess, Pgsql, inits[i].InitializerName()) + } + } + color.Info.Printf(InitSuccess, Pgsql) + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_sqlite.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_sqlite.go new file mode 100644 index 000000000..d7a97eba5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_initdb_sqlite.go @@ -0,0 +1,88 @@ +package system + +import ( + "context" + "errors" + "github.com/glebarez/sqlite" + "github.com/gofrs/uuid/v5" + "github.com/gookit/color" + "gorm.io/gorm" + "path/filepath" + + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/flipped-aurora/gin-vue-admin/server/utils" +) + +type SqliteInitHandler struct{} + +func NewSqliteInitHandler() *SqliteInitHandler { + return &SqliteInitHandler{} +} + +// WriteConfig mysql回写配置 +func (h SqliteInitHandler) WriteConfig(ctx context.Context) error { + c, ok := ctx.Value("config").(config.Sqlite) + if !ok { + return errors.New("sqlite config invalid") + } + global.GVA_CONFIG.System.DbType = "sqlite" + global.GVA_CONFIG.Sqlite = c + global.GVA_CONFIG.JWT.SigningKey = uuid.Must(uuid.NewV4()).String() + cs := utils.StructToMap(global.GVA_CONFIG) + for k, v := range cs { + global.GVA_VP.Set(k, v) + } + global.GVA_ACTIVE_DBNAME = &c.Dbname + return global.GVA_VP.WriteConfig() +} + +// EnsureDB 创建数据库并初始化 sqlite +func (h SqliteInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) { + if s, ok := ctx.Value("dbtype").(string); !ok || s != "sqlite" { + return ctx, ErrDBTypeMismatch + } + + c := conf.ToSqliteConfig() + next = context.WithValue(ctx, "config", c) + if c.Dbname == "" { + return ctx, nil + } // 如果没有数据库名, 则跳出初始化数据 + + dsn := conf.SqliteEmptyDsn() + + var db *gorm.DB + if db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + }); err != nil { + return ctx, err + } + global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..") + next = context.WithValue(next, "db", db) + return next, err +} + +func (h SqliteInitHandler) InitTables(ctx context.Context, inits initSlice) error { + return createTables(ctx, inits) +} + +func (h SqliteInitHandler) InitData(ctx context.Context, inits initSlice) error { + next, cancel := context.WithCancel(ctx) + defer func(c func()) { c() }(cancel) + for _, init := range inits { + if init.DataInserted(next) { + color.Info.Printf(InitDataExist, Sqlite, init.InitializerName()) + continue + } + if n, err := init.InitializeData(next); err != nil { + color.Info.Printf(InitDataFailed, Sqlite, init.InitializerName(), err) + return err + } else { + next = n + color.Info.Printf(InitDataSuccess, Sqlite, init.InitializerName()) + } + } + color.Info.Printf(InitSuccess, Sqlite) + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_menu.go new file mode 100644 index 000000000..4d5a0ea71 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_menu.go @@ -0,0 +1,289 @@ +package system + +import ( + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "gorm.io/gorm" + "strconv" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: getMenuTreeMap +//@description: 获取路由总树map +//@param: authorityId string +//@return: treeMap map[string][]system.SysMenu, err error + +type MenuService struct{} + +var MenuServiceApp = new(MenuService) + +func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[uint][]system.SysMenu, err error) { + var allMenus []system.SysMenu + var baseMenu []system.SysBaseMenu + var btns []system.SysAuthorityBtn + treeMap = make(map[uint][]system.SysMenu) + + var SysAuthorityMenus []system.SysAuthorityMenu + err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&SysAuthorityMenus).Error + if err != nil { + return + } + + var MenuIds []string + + for i := range SysAuthorityMenus { + MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId) + } + + err = global.GVA_DB.Where("id in (?)", MenuIds).Order("sort").Preload("Parameters").Find(&baseMenu).Error + if err != nil { + return + } + + for i := range baseMenu { + allMenus = append(allMenus, system.SysMenu{ + SysBaseMenu: baseMenu[i], + AuthorityId: authorityId, + MenuId: baseMenu[i].ID, + Parameters: baseMenu[i].Parameters, + }) + } + + err = global.GVA_DB.Where("authority_id = ?", authorityId).Preload("SysBaseMenuBtn").Find(&btns).Error + if err != nil { + return + } + var btnMap = make(map[uint]map[string]uint) + for _, v := range btns { + if btnMap[v.SysMenuID] == nil { + btnMap[v.SysMenuID] = make(map[string]uint) + } + btnMap[v.SysMenuID][v.SysBaseMenuBtn.Name] = authorityId + } + for _, v := range allMenus { + v.Btns = btnMap[v.SysBaseMenu.ID] + treeMap[v.ParentId] = append(treeMap[v.ParentId], v) + } + return treeMap, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetMenuTree +//@description: 获取动态菜单树 +//@param: authorityId string +//@return: menus []system.SysMenu, err error + +func (menuService *MenuService) GetMenuTree(authorityId uint) (menus []system.SysMenu, err error) { + menuTree, err := menuService.getMenuTreeMap(authorityId) + menus = menuTree[0] + for i := 0; i < len(menus); i++ { + err = menuService.getChildrenList(&menus[i], menuTree) + } + return menus, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: getChildrenList +//@description: 获取子菜单 +//@param: menu *model.SysMenu, treeMap map[string][]model.SysMenu +//@return: err error + +func (menuService *MenuService) getChildrenList(menu *system.SysMenu, treeMap map[uint][]system.SysMenu) (err error) { + menu.Children = treeMap[menu.MenuId] + for i := 0; i < len(menu.Children); i++ { + err = menuService.getChildrenList(&menu.Children[i], treeMap) + } + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetInfoList +//@description: 获取路由分页 +//@return: list interface{}, total int64,err error + +func (menuService *MenuService) GetInfoList(authorityID uint) (list interface{}, err error) { + var menuList []system.SysBaseMenu + treeMap, err := menuService.getBaseMenuTreeMap(authorityID) + menuList = treeMap[0] + for i := 0; i < len(menuList); i++ { + err = menuService.getBaseChildrenList(&menuList[i], treeMap) + } + return menuList, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: getBaseChildrenList +//@description: 获取菜单的子菜单 +//@param: menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu +//@return: err error + +func (menuService *MenuService) getBaseChildrenList(menu *system.SysBaseMenu, treeMap map[uint][]system.SysBaseMenu) (err error) { + menu.Children = treeMap[menu.ID] + for i := 0; i < len(menu.Children); i++ { + err = menuService.getBaseChildrenList(&menu.Children[i], treeMap) + } + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: AddBaseMenu +//@description: 添加基础路由 +//@param: menu model.SysBaseMenu +//@return: error + +func (menuService *MenuService) AddBaseMenu(menu system.SysBaseMenu) error { + if !errors.Is(global.GVA_DB.Where("name = ?", menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在重复name,请修改name") + } + return global.GVA_DB.Create(&menu).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: getBaseMenuTreeMap +//@description: 获取路由总树map +//@return: treeMap map[string][]system.SysBaseMenu, err error + +func (menuService *MenuService) getBaseMenuTreeMap(authorityID uint) (treeMap map[uint][]system.SysBaseMenu, err error) { + parentAuthorityID, err := AuthorityServiceApp.GetParentAuthorityID(authorityID) + if err != nil { + return nil, err + } + + var allMenus []system.SysBaseMenu + treeMap = make(map[uint][]system.SysBaseMenu) + db := global.GVA_DB.Order("sort").Preload("MenuBtn").Preload("Parameters") + + // 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选 + if global.GVA_CONFIG.System.UseStrictAuth && parentAuthorityID != 0 { + var authorityMenus []system.SysAuthorityMenu + err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityID).Find(&authorityMenus).Error + if err != nil { + return nil, err + } + var menuIds []string + for i := range authorityMenus { + menuIds = append(menuIds, authorityMenus[i].MenuId) + } + db = db.Where("id in (?)", menuIds) + } + + err = db.Find(&allMenus).Error + for _, v := range allMenus { + treeMap[v.ParentId] = append(treeMap[v.ParentId], v) + } + return treeMap, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetBaseMenuTree +//@description: 获取基础路由树 +//@return: menus []system.SysBaseMenu, err error + +func (menuService *MenuService) GetBaseMenuTree(authorityID uint) (menus []system.SysBaseMenu, err error) { + treeMap, err := menuService.getBaseMenuTreeMap(authorityID) + menus = treeMap[0] + for i := 0; i < len(menus); i++ { + err = menuService.getBaseChildrenList(&menus[i], treeMap) + } + return menus, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: AddMenuAuthority +//@description: 为角色增加menu树 +//@param: menus []model.SysBaseMenu, authorityId string +//@return: err error + +func (menuService *MenuService) AddMenuAuthority(menus []system.SysBaseMenu, adminAuthorityID, authorityId uint) (err error) { + var auth system.SysAuthority + auth.AuthorityId = authorityId + auth.SysBaseMenus = menus + + err = AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, authorityId) + if err != nil { + return err + } + + var authority system.SysAuthority + _ = global.GVA_DB.First(&authority, "authority_id = ?", adminAuthorityID).Error + var menuIds []string + + // 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选 + if global.GVA_CONFIG.System.UseStrictAuth && *authority.ParentId != 0 { + var authorityMenus []system.SysAuthorityMenu + err = global.GVA_DB.Where("sys_authority_authority_id = ?", adminAuthorityID).Find(&authorityMenus).Error + if err != nil { + return err + } + for i := range authorityMenus { + menuIds = append(menuIds, authorityMenus[i].MenuId) + } + + for i := range menus { + hasMenu := false + for j := range menuIds { + idStr := strconv.Itoa(int(menus[i].ID)) + if idStr == menuIds[j] { + hasMenu = true + } + } + if !hasMenu { + return errors.New("添加失败,请勿跨级操作") + } + } + } + + err = AuthorityServiceApp.SetMenuAuthority(&auth) + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetMenuAuthority +//@description: 查看当前角色树 +//@param: info *request.GetAuthorityId +//@return: menus []system.SysMenu, err error + +func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (menus []system.SysMenu, err error) { + var baseMenu []system.SysBaseMenu + var SysAuthorityMenus []system.SysAuthorityMenu + err = global.GVA_DB.Where("sys_authority_authority_id = ?", info.AuthorityId).Find(&SysAuthorityMenus).Error + if err != nil { + return + } + + var MenuIds []string + + for i := range SysAuthorityMenus { + MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId) + } + + err = global.GVA_DB.Where("id in (?) ", MenuIds).Order("sort").Find(&baseMenu).Error + + for i := range baseMenu { + menus = append(menus, system.SysMenu{ + SysBaseMenu: baseMenu[i], + AuthorityId: info.AuthorityId, + MenuId: baseMenu[i].ID, + Parameters: baseMenu[i].Parameters, + }) + } + return menus, err +} + +// UserAuthorityDefaultRouter 用户角色默认路由检查 +// +// Author [SliverHorn](https://github.com/SliverHorn) +func (menuService *MenuService) UserAuthorityDefaultRouter(user *system.SysUser) { + var menuIds []string + err := global.GVA_DB.Model(&system.SysAuthorityMenu{}).Where("sys_authority_authority_id = ?", user.AuthorityId).Pluck("sys_base_menu_id", &menuIds).Error + if err != nil { + return + } + var am system.SysBaseMenu + err = global.GVA_DB.First(&am, "name = ? and id in (?)", user.Authority.DefaultRouter, menuIds).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + user.Authority.DefaultRouter = "404" + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_operation_record.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_operation_record.go new file mode 100644 index 000000000..adfc25efd --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_operation_record.go @@ -0,0 +1,88 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" +) + +//@author: [granty1](https://github.com/granty1) +//@function: CreateSysOperationRecord +//@description: 创建记录 +//@param: sysOperationRecord model.SysOperationRecord +//@return: err error + +type OperationRecordService struct{} + +var OperationRecordServiceApp = new(OperationRecordService) + +func (operationRecordService *OperationRecordService) CreateSysOperationRecord(sysOperationRecord system.SysOperationRecord) (err error) { + err = global.GVA_DB.Create(&sysOperationRecord).Error + return err +} + +//@author: [granty1](https://github.com/granty1) +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteSysOperationRecordByIds +//@description: 批量删除记录 +//@param: ids request.IdsReq +//@return: err error + +func (operationRecordService *OperationRecordService) DeleteSysOperationRecordByIds(ids request.IdsReq) (err error) { + err = global.GVA_DB.Delete(&[]system.SysOperationRecord{}, "id in (?)", ids.Ids).Error + return err +} + +//@author: [granty1](https://github.com/granty1) +//@function: DeleteSysOperationRecord +//@description: 删除操作记录 +//@param: sysOperationRecord model.SysOperationRecord +//@return: err error + +func (operationRecordService *OperationRecordService) DeleteSysOperationRecord(sysOperationRecord system.SysOperationRecord) (err error) { + err = global.GVA_DB.Delete(&sysOperationRecord).Error + return err +} + +//@author: [granty1](https://github.com/granty1) +//@function: GetSysOperationRecord +//@description: 根据id获取单条操作记录 +//@param: id uint +//@return: sysOperationRecord system.SysOperationRecord, err error + +func (operationRecordService *OperationRecordService) GetSysOperationRecord(id uint) (sysOperationRecord system.SysOperationRecord, err error) { + err = global.GVA_DB.Where("id = ?", id).First(&sysOperationRecord).Error + return +} + +//@author: [granty1](https://github.com/granty1) +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetSysOperationRecordInfoList +//@description: 分页获取操作记录列表 +//@param: info systemReq.SysOperationRecordSearch +//@return: list interface{}, total int64, err error + +func (operationRecordService *OperationRecordService) GetSysOperationRecordInfoList(info systemReq.SysOperationRecordSearch) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := global.GVA_DB.Model(&system.SysOperationRecord{}) + var sysOperationRecords []system.SysOperationRecord + // 如果有条件搜索 下方会自动创建搜索语句 + if info.Method != "" { + db = db.Where("method = ?", info.Method) + } + if info.Path != "" { + db = db.Where("path LIKE ?", "%"+info.Path+"%") + } + if info.Status != 0 { + db = db.Where("status = ?", info.Status) + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Order("id desc").Limit(limit).Offset(offset).Preload("User").Find(&sysOperationRecords).Error + return sysOperationRecords, total, err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_params.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_params.go new file mode 100644 index 000000000..7391ec016 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_params.go @@ -0,0 +1,82 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" +) + +type SysParamsService struct{} + +// CreateSysParams 创建参数记录 +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) CreateSysParams(sysParams *system.SysParams) (err error) { + err = global.GVA_DB.Create(sysParams).Error + return err +} + +// DeleteSysParams 删除参数记录 +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) DeleteSysParams(ID string) (err error) { + err = global.GVA_DB.Delete(&system.SysParams{}, "id = ?", ID).Error + return err +} + +// DeleteSysParamsByIds 批量删除参数记录 +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) DeleteSysParamsByIds(IDs []string) (err error) { + err = global.GVA_DB.Delete(&[]system.SysParams{}, "id in ?", IDs).Error + return err +} + +// UpdateSysParams 更新参数记录 +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) UpdateSysParams(sysParams system.SysParams) (err error) { + err = global.GVA_DB.Model(&system.SysParams{}).Where("id = ?", sysParams.ID).Updates(&sysParams).Error + return err +} + +// GetSysParams 根据ID获取参数记录 +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) GetSysParams(ID string) (sysParams system.SysParams, err error) { + err = global.GVA_DB.Where("id = ?", ID).First(&sysParams).Error + return +} + +// GetSysParamsInfoList 分页获取参数记录 +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) GetSysParamsInfoList(info systemReq.SysParamsSearch) (list []system.SysParams, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := global.GVA_DB.Model(&system.SysParams{}) + var sysParamss []system.SysParams + // 如果有条件搜索 下方会自动创建搜索语句 + if info.StartCreatedAt != nil && info.EndCreatedAt != nil { + db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + if info.Key != "" { + db = db.Where("key LIKE ?", "%"+info.Key+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + + if limit != 0 { + db = db.Limit(limit).Offset(offset) + } + + err = db.Find(&sysParamss).Error + return sysParamss, total, err +} + +// GetSysParam 根据key获取参数value +// Author [Mr.奇淼](https://github.com/pixelmaxQm) +func (sysParamsService *SysParamsService) GetSysParam(key string) (param system.SysParams, err error) { + err = global.GVA_DB.Where(system.SysParams{Key: key}).First(¶m).Error + return +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_system.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_system.go new file mode 100644 index 000000000..a4415f041 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_system.go @@ -0,0 +1,62 @@ +package system + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/config" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "go.uber.org/zap" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetSystemConfig +//@description: 读取配置文件 +//@return: conf config.Server, err error + +type SystemConfigService struct{} + +var SystemConfigServiceApp = new(SystemConfigService) + +func (systemConfigService *SystemConfigService) GetSystemConfig() (conf config.Server, err error) { + return global.GVA_CONFIG, nil +} + +// @description set system config, +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetSystemConfig +//@description: 设置配置文件 +//@param: system model.System +//@return: err error + +func (systemConfigService *SystemConfigService) SetSystemConfig(system system.System) (err error) { + cs := utils.StructToMap(system.Config) + for k, v := range cs { + global.GVA_VP.Set(k, v) + } + err = global.GVA_VP.WriteConfig() + return err +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: GetServerInfo +//@description: 获取服务器信息 +//@return: server *utils.Server, err error + +func (systemConfigService *SystemConfigService) GetServerInfo() (server *utils.Server, err error) { + var s utils.Server + s.Os = utils.InitOS() + if s.Cpu, err = utils.InitCPU(); err != nil { + global.GVA_LOG.Error("func utils.InitCPU() Failed", zap.String("err", err.Error())) + return &s, err + } + if s.Ram, err = utils.InitRAM(); err != nil { + global.GVA_LOG.Error("func utils.InitRAM() Failed", zap.String("err", err.Error())) + return &s, err + } + if s.Disk, err = utils.InitDisk(); err != nil { + global.GVA_LOG.Error("func utils.InitDisk() Failed", zap.String("err", err.Error())) + return &s, err + } + + return &s, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_user.go new file mode 100644 index 000000000..65a94bbe8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/service/system/sys_user.go @@ -0,0 +1,317 @@ +package system + +import ( + "errors" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/model/common" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gofrs/uuid/v5" + "gorm.io/gorm" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Register +//@description: 用户注册 +//@param: u model.SysUser +//@return: userInter system.SysUser, err error + +type UserService struct{} + +var UserServiceApp = new(UserService) + +func (userService *UserService) Register(u system.SysUser) (userInter system.SysUser, err error) { + var user system.SysUser + if !errors.Is(global.GVA_DB.Where("username = ?", u.Username).First(&user).Error, gorm.ErrRecordNotFound) { // 判断用户名是否注册 + return userInter, errors.New("用户名已注册") + } + // 否则 附加uuid 密码hash加密 注册 + u.Password = utils.BcryptHash(u.Password) + u.UUID = uuid.Must(uuid.NewV4()) + err = global.GVA_DB.Create(&u).Error + return u, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: Login +//@description: 用户登录 +//@param: u *model.SysUser +//@return: err error, userInter *model.SysUser + +func (userService *UserService) Login(u *system.SysUser) (userInter *system.SysUser, err error) { + if nil == global.GVA_DB { + return nil, fmt.Errorf("db not init") + } + + var user system.SysUser + err = global.GVA_DB.Where("username = ?", u.Username).Preload("Authorities").Preload("Authority").First(&user).Error + if err == nil { + if ok := utils.BcryptCheck(u.Password, user.Password); !ok { + return nil, errors.New("密码错误") + } + MenuServiceApp.UserAuthorityDefaultRouter(&user) + } + return &user, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: ChangePassword +//@description: 修改用户密码 +//@param: u *model.SysUser, newPassword string +//@return: userInter *model.SysUser,err error + +func (userService *UserService) ChangePassword(u *system.SysUser, newPassword string) (userInter *system.SysUser, err error) { + var user system.SysUser + if err = global.GVA_DB.Where("id = ?", u.ID).First(&user).Error; err != nil { + return nil, err + } + if ok := utils.BcryptCheck(u.Password, user.Password); !ok { + return nil, errors.New("原密码错误") + } + user.Password = utils.BcryptHash(newPassword) + err = global.GVA_DB.Save(&user).Error + return &user, err + +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: GetUserInfoList +//@description: 分页获取数据 +//@param: info request.PageInfo +//@return: err error, list interface{}, total int64 + +func (userService *UserService) GetUserInfoList(info systemReq.GetUserList) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + db := global.GVA_DB.Model(&system.SysUser{}) + var userList []system.SysUser + + if info.NickName != "" { + db = db.Where("nick_name LIKE ?", "%"+info.NickName+"%") + } + if info.Phone != "" { + db = db.Where("phone LIKE ?", "%"+info.Phone+"%") + } + if info.Username != "" { + db = db.Where("username LIKE ?", "%"+info.Username+"%") + } + if info.Email != "" { + db = db.Where("email LIKE ?", "%"+info.Email+"%") + } + + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Preload("Authorities").Preload("Authority").Find(&userList).Error + return userList, total, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetUserAuthority +//@description: 设置一个用户的权限 +//@param: uuid uuid.UUID, authorityId string +//@return: err error + +func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err error) { + + assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUserAuthority{}).Error + if errors.Is(assignErr, gorm.ErrRecordNotFound) { + return errors.New("该用户无此角色") + } + + var authority system.SysAuthority + err = global.GVA_DB.Where("authority_id = ?", authorityId).First(&authority).Error + if err != nil { + return err + } + var authorityMenu []system.SysAuthorityMenu + var authorityMenuIDs []string + err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&authorityMenu).Error + if err != nil { + return err + } + + for i := range authorityMenu { + authorityMenuIDs = append(authorityMenuIDs, authorityMenu[i].MenuId) + } + + var authorityMenus []system.SysBaseMenu + err = global.GVA_DB.Preload("Parameters").Where("id in (?)", authorityMenuIDs).Find(&authorityMenus).Error + if err != nil { + return err + } + hasMenu := false + for i := range authorityMenus { + if authorityMenus[i].Name == authority.DefaultRouter { + hasMenu = true + break + } + } + if !hasMenu { + return errors.New("找不到默认路由,无法切换本角色") + } + + err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", id).Update("authority_id", authorityId).Error + return err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetUserAuthorities +//@description: 设置一个用户的权限 +//@param: id uint, authorityIds []string +//@return: err error + +func (userService *UserService) SetUserAuthorities(adminAuthorityID, id uint, authorityIds []uint) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var user system.SysUser + TxErr := tx.Where("id = ?", id).First(&user).Error + if TxErr != nil { + global.GVA_LOG.Debug(TxErr.Error()) + return errors.New("查询用户数据失败") + } + TxErr = tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error + if TxErr != nil { + return TxErr + } + var useAuthority []system.SysUserAuthority + for _, v := range authorityIds { + e := AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, v) + if e != nil { + return e + } + useAuthority = append(useAuthority, system.SysUserAuthority{ + SysUserId: id, SysAuthorityAuthorityId: v, + }) + } + TxErr = tx.Create(&useAuthority).Error + if TxErr != nil { + return TxErr + } + TxErr = tx.Model(&user).Update("authority_id", authorityIds[0]).Error + if TxErr != nil { + return TxErr + } + // 返回 nil 提交事务 + return nil + }) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: DeleteUser +//@description: 删除用户 +//@param: id float64 +//@return: err error + +func (userService *UserService) DeleteUser(id int) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("id = ?", id).Delete(&system.SysUser{}).Error; err != nil { + return err + } + if err := tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error; err != nil { + return err + } + return nil + }) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetUserInfo +//@description: 设置用户信息 +//@param: reqUser model.SysUser +//@return: err error, user model.SysUser + +func (userService *UserService) SetUserInfo(req system.SysUser) error { + return global.GVA_DB.Model(&system.SysUser{}). + Select("updated_at", "nick_name", "header_img", "phone", "email", "enable"). + Where("id=?", req.ID). + Updates(map[string]interface{}{ + "updated_at": time.Now(), + "nick_name": req.NickName, + "header_img": req.HeaderImg, + "phone": req.Phone, + "email": req.Email, + "enable": req.Enable, + }).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetSelfInfo +//@description: 设置用户信息 +//@param: reqUser model.SysUser +//@return: err error, user model.SysUser + +func (userService *UserService) SetSelfInfo(req system.SysUser) error { + return global.GVA_DB.Model(&system.SysUser{}). + Where("id=?", req.ID). + Updates(req).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: SetSelfSetting +//@description: 设置用户配置 +//@param: req datatypes.JSON, uid uint +//@return: err error + +func (userService *UserService) SetSelfSetting(req common.JSONMap, uid uint) error { + return global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", uid).Update("origin_setting", req).Error +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: GetUserInfo +//@description: 获取用户信息 +//@param: uuid uuid.UUID +//@return: err error, user system.SysUser + +func (userService *UserService) GetUserInfo(uuid uuid.UUID) (user system.SysUser, err error) { + var reqUser system.SysUser + err = global.GVA_DB.Preload("Authorities").Preload("Authority").First(&reqUser, "uuid = ?", uuid).Error + if err != nil { + return reqUser, err + } + MenuServiceApp.UserAuthorityDefaultRouter(&reqUser) + return reqUser, err +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: FindUserById +//@description: 通过id获取用户信息 +//@param: id int +//@return: err error, user *model.SysUser + +func (userService *UserService) FindUserById(id int) (user *system.SysUser, err error) { + var u system.SysUser + err = global.GVA_DB.Where("id = ?", id).First(&u).Error + return &u, err +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: FindUserByUuid +//@description: 通过uuid获取用户信息 +//@param: uuid string +//@return: err error, user *model.SysUser + +func (userService *UserService) FindUserByUuid(uuid string) (user *system.SysUser, err error) { + var u system.SysUser + if err = global.GVA_DB.Where("uuid = ?", uuid).First(&u).Error; err != nil { + return &u, errors.New("用户不存在") + } + return &u, nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: ResetPassword +//@description: 修改用户密码 +//@param: ID uint +//@return: err error + +func (userService *UserService) ResetPassword(ID uint) (err error) { + err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", ID).Update("password", utils.BcryptHash("123456")).Error + return err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/example/file_upload_download.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/example/file_upload_download.go new file mode 100644 index 000000000..bc6b16f6d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/example/file_upload_download.go @@ -0,0 +1,65 @@ +package example + +import ( + "context" + + "github.com/flipped-aurora/gin-vue-admin/server/model/example" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderExaFile = system.InitOrderInternal + 1 + +type initExaFileMysql struct{} + +// auto run +func init() { + system.RegisterInit(initOrderExaFile, &initExaFileMysql{}) +} + +func (i *initExaFileMysql) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&example.ExaFileUploadAndDownload{}) +} + +func (i *initExaFileMysql) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&example.ExaFileUploadAndDownload{}) +} + +func (i initExaFileMysql) InitializerName() string { + return example.ExaFileUploadAndDownload{}.TableName() +} + +func (i *initExaFileMysql) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + entities := []example.ExaFileUploadAndDownload{ + {Name: "neko", Url: "https://pic4.zhimg.com/100/v2-71172f3051b5f85c32d51a7a35bc3399_qhd.jpg", Tag: "png", Key: "158787308910.png"}, + } + if err := db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, example.ExaFileUploadAndDownload{}.TableName()+"表数据初始化失败!") + } + return ctx, nil +} + +func (i *initExaFileMysql) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + lookup := example.ExaFileUploadAndDownload{Name: "logo.png", Key: "1587973709logo.png"} + if errors.Is(db.First(&lookup, &lookup).Error, gorm.ErrRecordNotFound) { + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/api.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/api.go new file mode 100644 index 000000000..89c591d48 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/api.go @@ -0,0 +1,203 @@ +package system + +import ( + "context" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type initApi struct{} + +const initOrderApi = system.InitOrderSystem + 1 + +// auto run +func init() { + system.RegisterInit(initOrderApi, &initApi{}) +} + +func (i initApi) InitializerName() string { + return sysModel.SysApi{}.TableName() +} + +func (i *initApi) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysApi{}) +} + +func (i *initApi) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysApi{}) +} + +func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + entities := []sysModel.SysApi{ + {ApiGroup: "jwt", Method: "POST", Path: "/jwt/jsonInBlacklist", Description: "jwt加入黑名单(退出,必选)"}, + + {ApiGroup: "系统用户", Method: "DELETE", Path: "/user/deleteUser", Description: "删除用户"}, + {ApiGroup: "系统用户", Method: "POST", Path: "/user/admin_register", Description: "用户注册"}, + {ApiGroup: "系统用户", Method: "POST", Path: "/user/getUserList", Description: "获取用户列表"}, + {ApiGroup: "系统用户", Method: "PUT", Path: "/user/setUserInfo", Description: "设置用户信息"}, + {ApiGroup: "系统用户", Method: "PUT", Path: "/user/setSelfInfo", Description: "设置自身信息(必选)"}, + {ApiGroup: "系统用户", Method: "GET", Path: "/user/getUserInfo", Description: "获取自身信息(必选)"}, + {ApiGroup: "系统用户", Method: "POST", Path: "/user/setUserAuthorities", Description: "设置权限组"}, + {ApiGroup: "系统用户", Method: "POST", Path: "/user/changePassword", Description: "修改密码(建议选择)"}, + {ApiGroup: "系统用户", Method: "POST", Path: "/user/setUserAuthority", Description: "修改用户角色(必选)"}, + {ApiGroup: "系统用户", Method: "POST", Path: "/user/resetPassword", Description: "重置用户密码"}, + {ApiGroup: "系统用户", Method: "PUT", Path: "/user/setSelfSetting", Description: "用户界面配置"}, + + {ApiGroup: "api", Method: "POST", Path: "/api/createApi", Description: "创建api"}, + {ApiGroup: "api", Method: "POST", Path: "/api/deleteApi", Description: "删除Api"}, + {ApiGroup: "api", Method: "POST", Path: "/api/updateApi", Description: "更新Api"}, + {ApiGroup: "api", Method: "POST", Path: "/api/getApiList", Description: "获取api列表"}, + {ApiGroup: "api", Method: "POST", Path: "/api/getAllApis", Description: "获取所有api"}, + {ApiGroup: "api", Method: "POST", Path: "/api/getApiById", Description: "获取api详细信息"}, + {ApiGroup: "api", Method: "DELETE", Path: "/api/deleteApisByIds", Description: "批量删除api"}, + {ApiGroup: "api", Method: "GET", Path: "/api/syncApi", Description: "获取待同步API"}, + {ApiGroup: "api", Method: "GET", Path: "/api/getApiGroups", Description: "获取路由组"}, + {ApiGroup: "api", Method: "POST", Path: "/api/enterSyncApi", Description: "确认同步API"}, + {ApiGroup: "api", Method: "POST", Path: "/api/ignoreApi", Description: "忽略API"}, + + {ApiGroup: "角色", Method: "POST", Path: "/authority/copyAuthority", Description: "拷贝角色"}, + {ApiGroup: "角色", Method: "POST", Path: "/authority/createAuthority", Description: "创建角色"}, + {ApiGroup: "角色", Method: "POST", Path: "/authority/deleteAuthority", Description: "删除角色"}, + {ApiGroup: "角色", Method: "PUT", Path: "/authority/updateAuthority", Description: "更新角色信息"}, + {ApiGroup: "角色", Method: "POST", Path: "/authority/getAuthorityList", Description: "获取角色列表"}, + {ApiGroup: "角色", Method: "POST", Path: "/authority/setDataAuthority", Description: "设置角色资源权限"}, + + {ApiGroup: "casbin", Method: "POST", Path: "/casbin/updateCasbin", Description: "更改角色api权限"}, + {ApiGroup: "casbin", Method: "POST", Path: "/casbin/getPolicyPathByAuthorityId", Description: "获取权限列表"}, + + {ApiGroup: "菜单", Method: "POST", Path: "/menu/addBaseMenu", Description: "新增菜单"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/getMenu", Description: "获取菜单树(必选)"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/deleteBaseMenu", Description: "删除菜单"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/updateBaseMenu", Description: "更新菜单"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/getBaseMenuById", Description: "根据id获取菜单"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/getMenuList", Description: "分页获取基础menu列表"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/getBaseMenuTree", Description: "获取用户动态路由"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/getMenuAuthority", Description: "获取指定角色menu"}, + {ApiGroup: "菜单", Method: "POST", Path: "/menu/addMenuAuthority", Description: "增加menu和角色关联关系"}, + + {ApiGroup: "分片上传", Method: "GET", Path: "/fileUploadAndDownload/findFile", Description: "寻找目标文件(秒传)"}, + {ApiGroup: "分片上传", Method: "POST", Path: "/fileUploadAndDownload/breakpointContinue", Description: "断点续传"}, + {ApiGroup: "分片上传", Method: "POST", Path: "/fileUploadAndDownload/breakpointContinueFinish", Description: "断点续传完成"}, + {ApiGroup: "分片上传", Method: "POST", Path: "/fileUploadAndDownload/removeChunk", Description: "上传完成移除文件"}, + + {ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/upload", Description: "文件上传(建议选择)"}, + {ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/deleteFile", Description: "删除文件"}, + {ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/editFileName", Description: "文件名或者备注编辑"}, + {ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/getFileList", Description: "获取上传文件列表"}, + {ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/importURL", Description: "导入URL"}, + + {ApiGroup: "系统服务", Method: "POST", Path: "/system/getServerInfo", Description: "获取服务器信息"}, + {ApiGroup: "系统服务", Method: "POST", Path: "/system/getSystemConfig", Description: "获取配置文件内容"}, + {ApiGroup: "系统服务", Method: "POST", Path: "/system/setSystemConfig", Description: "设置配置文件内容"}, + + {ApiGroup: "客户", Method: "PUT", Path: "/customer/customer", Description: "更新客户"}, + {ApiGroup: "客户", Method: "POST", Path: "/customer/customer", Description: "创建客户"}, + {ApiGroup: "客户", Method: "DELETE", Path: "/customer/customer", Description: "删除客户"}, + {ApiGroup: "客户", Method: "GET", Path: "/customer/customer", Description: "获取单一客户"}, + {ApiGroup: "客户", Method: "GET", Path: "/customer/customerList", Description: "获取客户列表"}, + + {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getDB", Description: "获取所有数据库"}, + {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getTables", Description: "获取数据库表"}, + {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/createTemp", Description: "自动化代码"}, + {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/preview", Description: "预览自动化代码"}, + {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getColumn", Description: "获取所选table的所有字段"}, + {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/installPlugin", Description: "安装插件"}, + {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/pubPlug", Description: "打包插件"}, + + {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/createPackage", Description: "配置模板"}, + {ApiGroup: "模板配置", Method: "GET", Path: "/autoCode/getTemplates", Description: "获取模板文件"}, + {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/getPackage", Description: "获取所有模板"}, + {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/delPackage", Description: "删除模板"}, + + {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/getMeta", Description: "获取meta信息"}, + {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/rollback", Description: "回滚自动生成代码"}, + {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/getSysHistory", Description: "查询回滚记录"}, + {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/delSysHistory", Description: "删除回滚记录"}, + {ApiGroup: "代码生成器历史", Method: "POST", Path: "/autoCode/addFunc", Description: "增加模板方法"}, + + {ApiGroup: "系统字典详情", Method: "PUT", Path: "/sysDictionaryDetail/updateSysDictionaryDetail", Description: "更新字典内容"}, + {ApiGroup: "系统字典详情", Method: "POST", Path: "/sysDictionaryDetail/createSysDictionaryDetail", Description: "新增字典内容"}, + {ApiGroup: "系统字典详情", Method: "DELETE", Path: "/sysDictionaryDetail/deleteSysDictionaryDetail", Description: "删除字典内容"}, + {ApiGroup: "系统字典详情", Method: "GET", Path: "/sysDictionaryDetail/findSysDictionaryDetail", Description: "根据ID获取字典内容"}, + {ApiGroup: "系统字典详情", Method: "GET", Path: "/sysDictionaryDetail/getSysDictionaryDetailList", Description: "获取字典内容列表"}, + + {ApiGroup: "系统字典", Method: "POST", Path: "/sysDictionary/createSysDictionary", Description: "新增字典"}, + {ApiGroup: "系统字典", Method: "DELETE", Path: "/sysDictionary/deleteSysDictionary", Description: "删除字典"}, + {ApiGroup: "系统字典", Method: "PUT", Path: "/sysDictionary/updateSysDictionary", Description: "更新字典"}, + {ApiGroup: "系统字典", Method: "GET", Path: "/sysDictionary/findSysDictionary", Description: "根据ID获取字典(建议选择)"}, + {ApiGroup: "系统字典", Method: "GET", Path: "/sysDictionary/getSysDictionaryList", Description: "获取字典列表"}, + + {ApiGroup: "操作记录", Method: "POST", Path: "/sysOperationRecord/createSysOperationRecord", Description: "新增操作记录"}, + {ApiGroup: "操作记录", Method: "GET", Path: "/sysOperationRecord/findSysOperationRecord", Description: "根据ID获取操作记录"}, + {ApiGroup: "操作记录", Method: "GET", Path: "/sysOperationRecord/getSysOperationRecordList", Description: "获取操作记录列表"}, + {ApiGroup: "操作记录", Method: "DELETE", Path: "/sysOperationRecord/deleteSysOperationRecord", Description: "删除操作记录"}, + {ApiGroup: "操作记录", Method: "DELETE", Path: "/sysOperationRecord/deleteSysOperationRecordByIds", Description: "批量删除操作历史"}, + + {ApiGroup: "断点续传(插件版)", Method: "POST", Path: "/simpleUploader/upload", Description: "插件版分片上传"}, + {ApiGroup: "断点续传(插件版)", Method: "GET", Path: "/simpleUploader/checkFileMd5", Description: "文件完整度验证"}, + {ApiGroup: "断点续传(插件版)", Method: "GET", Path: "/simpleUploader/mergeFileMd5", Description: "上传完成合并文件"}, + + {ApiGroup: "email", Method: "POST", Path: "/email/emailTest", Description: "发送测试邮件"}, + {ApiGroup: "email", Method: "POST", Path: "/email/sendEmail", Description: "发送邮件"}, + + {ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/setAuthorityBtn", Description: "设置按钮权限"}, + {ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/getAuthorityBtn", Description: "获取已有按钮权限"}, + {ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/canRemoveAuthorityBtn", Description: "删除按钮"}, + + {ApiGroup: "表格模板", Method: "POST", Path: "/sysExportTemplate/createSysExportTemplate", Description: "新增导出模板"}, + {ApiGroup: "表格模板", Method: "DELETE", Path: "/sysExportTemplate/deleteSysExportTemplate", Description: "删除导出模板"}, + {ApiGroup: "表格模板", Method: "DELETE", Path: "/sysExportTemplate/deleteSysExportTemplateByIds", Description: "批量删除导出模板"}, + {ApiGroup: "表格模板", Method: "PUT", Path: "/sysExportTemplate/updateSysExportTemplate", Description: "更新导出模板"}, + {ApiGroup: "表格模板", Method: "GET", Path: "/sysExportTemplate/findSysExportTemplate", Description: "根据ID获取导出模板"}, + {ApiGroup: "表格模板", Method: "GET", Path: "/sysExportTemplate/getSysExportTemplateList", Description: "获取导出模板列表"}, + {ApiGroup: "表格模板", Method: "GET", Path: "/sysExportTemplate/exportExcel", Description: "导出Excel"}, + {ApiGroup: "表格模板", Method: "GET", Path: "/sysExportTemplate/exportTemplate", Description: "下载模板"}, + {ApiGroup: "表格模板", Method: "POST", Path: "/sysExportTemplate/importExcel", Description: "导入Excel"}, + + {ApiGroup: "公告", Method: "POST", Path: "/info/createInfo", Description: "新建公告"}, + {ApiGroup: "公告", Method: "DELETE", Path: "/info/deleteInfo", Description: "删除公告"}, + {ApiGroup: "公告", Method: "DELETE", Path: "/info/deleteInfoByIds", Description: "批量删除公告"}, + {ApiGroup: "公告", Method: "PUT", Path: "/info/updateInfo", Description: "更新公告"}, + {ApiGroup: "公告", Method: "GET", Path: "/info/findInfo", Description: "根据ID获取公告"}, + {ApiGroup: "公告", Method: "GET", Path: "/info/getInfoList", Description: "获取公告列表"}, + + {ApiGroup: "参数管理", Method: "POST", Path: "/sysParams/createSysParams", Description: "新建参数"}, + {ApiGroup: "参数管理", Method: "DELETE", Path: "/sysParams/deleteSysParams", Description: "删除参数"}, + {ApiGroup: "参数管理", Method: "DELETE", Path: "/sysParams/deleteSysParamsByIds", Description: "批量删除参数"}, + {ApiGroup: "参数管理", Method: "PUT", Path: "/sysParams/updateSysParams", Description: "更新参数"}, + {ApiGroup: "参数管理", Method: "GET", Path: "/sysParams/findSysParams", Description: "根据ID获取参数"}, + {ApiGroup: "参数管理", Method: "GET", Path: "/sysParams/getSysParamsList", Description: "获取参数列表"}, + {ApiGroup: "参数管理", Method: "GET", Path: "/sysParams/getSysParam", Description: "获取参数列表"}, + } + if err := db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, sysModel.SysApi{}.TableName()+"表数据初始化失败!") + } + next := context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initApi) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.Where("path = ? AND method = ?", "/authorityBtn/canRemoveAuthorityBtn", "POST"). + First(&sysModel.SysApi{}).Error, gorm.ErrRecordNotFound) { + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/api_ignore.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/api_ignore.go new file mode 100644 index 000000000..284a1cc0c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/api_ignore.go @@ -0,0 +1,77 @@ +package system + +import ( + "context" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type initApiIgnore struct{} + +const initOrderApiIgnore = initOrderApi + 1 + +// auto run +func init() { + system.RegisterInit(initOrderApiIgnore, &initApiIgnore{}) +} + +func (i initApiIgnore) InitializerName() string { + return sysModel.SysIgnoreApi{}.TableName() +} + +func (i *initApiIgnore) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysIgnoreApi{}) +} + +func (i *initApiIgnore) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysIgnoreApi{}) +} + +func (i *initApiIgnore) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + entities := []sysModel.SysIgnoreApi{ + {Method: "GET", Path: "/swagger/*any"}, + {Method: "GET", Path: "/api/freshCasbin"}, + {Method: "GET", Path: "/uploads/file/*filepath"}, + {Method: "GET", Path: "/health"}, + {Method: "HEAD", Path: "/uploads/file/*filepath"}, + {Method: "POST", Path: "/autoCode/llmAuto"}, + {Method: "POST", Path: "/system/reloadSystem"}, + {Method: "POST", Path: "/base/login"}, + {Method: "POST", Path: "/base/captcha"}, + {Method: "POST", Path: "/init/initdb"}, + {Method: "POST", Path: "/init/checkdb"}, + {Method: "GET", Path: "/info/getInfoDataSource"}, + {Method: "GET", Path: "/info/getInfoPublic"}, + } + if err := db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, sysModel.SysIgnoreApi{}.TableName()+"表数据初始化失败!") + } + next := context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initApiIgnore) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.Where("path = ? AND method = ?", "/swagger/*any", "GET"). + First(&sysModel.SysIgnoreApi{}).Error, gorm.ErrRecordNotFound) { + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/authorities_menus.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/authorities_menus.go new file mode 100644 index 000000000..1c9058b88 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/authorities_menus.go @@ -0,0 +1,83 @@ +package system + +import ( + "context" + + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderMenuAuthority = initOrderMenu + initOrderAuthority + +type initMenuAuthority struct{} + +// auto run +func init() { + system.RegisterInit(initOrderMenuAuthority, &initMenuAuthority{}) +} + +func (i *initMenuAuthority) MigrateTable(ctx context.Context) (context.Context, error) { + return ctx, nil // do nothing +} + +func (i *initMenuAuthority) TableCreated(ctx context.Context) bool { + return false // always replace +} + +func (i initMenuAuthority) InitializerName() string { + return "sys_menu_authorities" +} + +func (i *initMenuAuthority) InitializeData(ctx context.Context) (next context.Context, err error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + authorities, ok := ctx.Value(initAuthority{}.InitializerName()).([]sysModel.SysAuthority) + if !ok { + return ctx, errors.Wrap(system.ErrMissingDependentContext, "创建 [菜单-权限] 关联失败, 未找到权限表初始化数据") + } + menus, ok := ctx.Value(initMenu{}.InitializerName()).([]sysModel.SysBaseMenu) + if !ok { + return next, errors.Wrap(errors.New(""), "创建 [菜单-权限] 关联失败, 未找到菜单表初始化数据") + } + next = ctx + // 888 + if err = db.Model(&authorities[0]).Association("SysBaseMenus").Replace(menus); err != nil { + return next, err + } + + // 8881 + menu8881 := menus[:2] + menu8881 = append(menu8881, menus[7]) + if err = db.Model(&authorities[1]).Association("SysBaseMenus").Replace(menu8881); err != nil { + return next, err + } + + // 9528 + if err = db.Model(&authorities[2]).Association("SysBaseMenus").Replace(menus[:11]); err != nil { + return next, err + } + if err = db.Model(&authorities[2]).Association("SysBaseMenus").Append(menus[12:17]); err != nil { + return next, err + } + return next, nil +} + +func (i *initMenuAuthority) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + auth := &sysModel.SysAuthority{} + if ret := db.Model(auth). + Where("authority_id = ?", 9528).Preload("SysBaseMenus").Find(auth); ret != nil { + if ret.Error != nil { + return false + } + return len(auth.SysBaseMenus) > 0 + } + return false +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/authority.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/authority.go new file mode 100644 index 000000000..0426f2a4f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/authority.go @@ -0,0 +1,88 @@ +package system + +import ( + "context" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderAuthority = initOrderCasbin + 1 + +type initAuthority struct{} + +// auto run +func init() { + system.RegisterInit(initOrderAuthority, &initAuthority{}) +} + +func (i *initAuthority) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysAuthority{}) +} + +func (i *initAuthority) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysAuthority{}) +} + +func (i initAuthority) InitializerName() string { + return sysModel.SysAuthority{}.TableName() +} + +func (i *initAuthority) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + entities := []sysModel.SysAuthority{ + {AuthorityId: 888, AuthorityName: "普通用户", ParentId: utils.Pointer[uint](0), DefaultRouter: "dashboard"}, + {AuthorityId: 9528, AuthorityName: "测试角色", ParentId: utils.Pointer[uint](0), DefaultRouter: "dashboard"}, + {AuthorityId: 8881, AuthorityName: "普通用户子角色", ParentId: utils.Pointer[uint](888), DefaultRouter: "dashboard"}, + } + + if err := db.Create(&entities).Error; err != nil { + return ctx, errors.Wrapf(err, "%s表数据初始化失败!", sysModel.SysAuthority{}.TableName()) + } + // data authority + if err := db.Model(&entities[0]).Association("DataAuthorityId").Replace( + []*sysModel.SysAuthority{ + {AuthorityId: 888}, + {AuthorityId: 9528}, + {AuthorityId: 8881}, + }); err != nil { + return ctx, errors.Wrapf(err, "%s表数据初始化失败!", + db.Model(&entities[0]).Association("DataAuthorityId").Relationship.JoinTable.Name) + } + if err := db.Model(&entities[1]).Association("DataAuthorityId").Replace( + []*sysModel.SysAuthority{ + {AuthorityId: 9528}, + {AuthorityId: 8881}, + }); err != nil { + return ctx, errors.Wrapf(err, "%s表数据初始化失败!", + db.Model(&entities[1]).Association("DataAuthorityId").Relationship.JoinTable.Name) + } + + next := context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initAuthority) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.Where("authority_id = ?", "8881"). + First(&sysModel.SysAuthority{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/casbin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/casbin.go new file mode 100644 index 000000000..5a9cdbec1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/casbin.go @@ -0,0 +1,289 @@ +package system + +import ( + "context" + + adapter "github.com/casbin/gorm-adapter/v3" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderCasbin = initOrderApiIgnore + 1 + +type initCasbin struct{} + +// auto run +func init() { + system.RegisterInit(initOrderCasbin, &initCasbin{}) +} + +func (i *initCasbin) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&adapter.CasbinRule{}) +} + +func (i *initCasbin) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&adapter.CasbinRule{}) +} + +func (i initCasbin) InitializerName() string { + var entity adapter.CasbinRule + return entity.TableName() +} + +func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + entities := []adapter.CasbinRule{ + {Ptype: "p", V0: "888", V1: "/user/admin_register", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/api/createApi", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/getApiList", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/getApiById", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/deleteApi", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/updateApi", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/getAllApis", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/deleteApisByIds", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/api/syncApi", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/api/getApiGroups", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/api/enterSyncApi", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/api/ignoreApi", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/authority/copyAuthority", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/authority/updateAuthority", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/authority/createAuthority", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/authority/deleteAuthority", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/authority/getAuthorityList", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/authority/setDataAuthority", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/menu/getMenu", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/getMenuList", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/addBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/getBaseMenuTree", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/addMenuAuthority", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/getMenuAuthority", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/deleteBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/updateBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/menu/getBaseMenuById", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/user/getUserInfo", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/user/setUserInfo", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/user/setSelfInfo", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/user/getUserList", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/user/deleteUser", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/user/changePassword", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/user/setUserAuthority", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/user/setUserAuthorities", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/user/resetPassword", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/user/setSelfSetting", V2: "PUT"}, + + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/findFile", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/breakpointContinueFinish", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/breakpointContinue", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/removeChunk", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/upload", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/deleteFile", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/editFileName", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/getFileList", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/fileUploadAndDownload/importURL", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/casbin/updateCasbin", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/casbin/getPolicyPathByAuthorityId", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/jwt/jsonInBlacklist", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/system/getSystemConfig", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/system/setSystemConfig", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/system/getServerInfo", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/customer/customer", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/customer/customerList", V2: "GET"}, + + {Ptype: "p", V0: "888", V1: "/autoCode/getDB", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/autoCode/getMeta", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/preview", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/getTables", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/autoCode/getColumn", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/autoCode/rollback", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/createTemp", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/delSysHistory", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/getSysHistory", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/createPackage", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/getTemplates", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/autoCode/getPackage", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/delPackage", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/createPlug", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/installPlugin", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/pubPlug", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/addFunc", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/createSysDictionaryDetail", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/getSysDictionaryDetailList", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/deleteSysDictionaryDetail", V2: "DELETE"}, + + {Ptype: "p", V0: "888", V1: "/sysDictionary/findSysDictionary", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysDictionary/updateSysDictionary", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/sysDictionary/getSysDictionaryList", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysDictionary/createSysDictionary", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/sysDictionary/deleteSysDictionary", V2: "DELETE"}, + + {Ptype: "p", V0: "888", V1: "/sysOperationRecord/findSysOperationRecord", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysOperationRecord/updateSysOperationRecord", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/sysOperationRecord/createSysOperationRecord", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/sysOperationRecord/getSysOperationRecordList", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysOperationRecord/deleteSysOperationRecord", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/sysOperationRecord/deleteSysOperationRecordByIds", V2: "DELETE"}, + + {Ptype: "p", V0: "888", V1: "/email/emailTest", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/email/sendEmail", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/simpleUploader/upload", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/simpleUploader/checkFileMd5", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/simpleUploader/mergeFileMd5", V2: "GET"}, + + {Ptype: "p", V0: "888", V1: "/authorityBtn/setAuthorityBtn", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/authorityBtn/getAuthorityBtn", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/authorityBtn/canRemoveAuthorityBtn", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/createSysExportTemplate", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/deleteSysExportTemplate", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/deleteSysExportTemplateByIds", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/updateSysExportTemplate", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/findSysExportTemplate", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/getSysExportTemplateList", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/exportExcel", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/exportTemplate", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysExportTemplate/importExcel", V2: "POST"}, + + {Ptype: "p", V0: "888", V1: "/info/createInfo", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/info/deleteInfo", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/info/deleteInfoByIds", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/info/updateInfo", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/info/findInfo", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/info/getInfoList", V2: "GET"}, + + {Ptype: "p", V0: "888", V1: "/sysParams/createSysParams", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/sysParams/deleteSysParams", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/sysParams/deleteSysParamsByIds", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/sysParams/updateSysParams", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/sysParams/findSysParams", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysParams/getSysParamsList", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/sysParams/getSysParam", V2: "GET"}, + + {Ptype: "p", V0: "8881", V1: "/user/admin_register", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/api/createApi", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/api/getApiList", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/api/getApiById", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/api/deleteApi", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/api/updateApi", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/api/getAllApis", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/authority/createAuthority", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/authority/deleteAuthority", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/authority/getAuthorityList", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/authority/setDataAuthority", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/getMenu", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/getMenuList", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/addBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/getBaseMenuTree", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/addMenuAuthority", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/getMenuAuthority", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/deleteBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/updateBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/menu/getBaseMenuById", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/user/changePassword", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/user/getUserList", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/user/setUserAuthority", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/fileUploadAndDownload/upload", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/fileUploadAndDownload/getFileList", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/fileUploadAndDownload/deleteFile", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/fileUploadAndDownload/editFileName", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/fileUploadAndDownload/importURL", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/casbin/updateCasbin", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/casbin/getPolicyPathByAuthorityId", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/jwt/jsonInBlacklist", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/system/getSystemConfig", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/system/setSystemConfig", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "POST"}, + {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "PUT"}, + {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "DELETE"}, + {Ptype: "p", V0: "8881", V1: "/customer/customer", V2: "GET"}, + {Ptype: "p", V0: "8881", V1: "/customer/customerList", V2: "GET"}, + {Ptype: "p", V0: "8881", V1: "/user/getUserInfo", V2: "GET"}, + + {Ptype: "p", V0: "9528", V1: "/user/admin_register", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/api/createApi", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/api/getApiList", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/api/getApiById", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/api/deleteApi", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/api/updateApi", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/api/getAllApis", V2: "POST"}, + + {Ptype: "p", V0: "9528", V1: "/authority/createAuthority", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/authority/deleteAuthority", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/authority/getAuthorityList", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/authority/setDataAuthority", V2: "POST"}, + + {Ptype: "p", V0: "9528", V1: "/menu/getMenu", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/getMenuList", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/addBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/getBaseMenuTree", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/addMenuAuthority", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/getMenuAuthority", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/deleteBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/updateBaseMenu", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/menu/getBaseMenuById", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/user/changePassword", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/user/getUserList", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/user/setUserAuthority", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/fileUploadAndDownload/upload", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/fileUploadAndDownload/getFileList", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/fileUploadAndDownload/deleteFile", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/fileUploadAndDownload/editFileName", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/fileUploadAndDownload/importURL", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/casbin/updateCasbin", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/casbin/getPolicyPathByAuthorityId", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/jwt/jsonInBlacklist", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/system/getSystemConfig", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/system/setSystemConfig", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "PUT"}, + {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "GET"}, + {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/customer/customer", V2: "DELETE"}, + {Ptype: "p", V0: "9528", V1: "/customer/customerList", V2: "GET"}, + {Ptype: "p", V0: "9528", V1: "/autoCode/createTemp", V2: "POST"}, + {Ptype: "p", V0: "9528", V1: "/user/getUserInfo", V2: "GET"}, + } + if err := db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, "Casbin 表 ("+i.InitializerName()+") 数据初始化失败!") + } + next := context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initCasbin) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.Where(adapter.CasbinRule{Ptype: "p", V0: "9528", V1: "/user/getUserInfo", V2: "GET"}). + First(&adapter.CasbinRule{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/dictionary.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/dictionary.go new file mode 100644 index 000000000..001496327 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/dictionary.go @@ -0,0 +1,71 @@ +package system + +import ( + "context" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderDict = initOrderCasbin + 1 + +type initDict struct{} + +// auto run +func init() { + system.RegisterInit(initOrderDict, &initDict{}) +} + +func (i *initDict) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysDictionary{}) +} + +func (i *initDict) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysDictionary{}) +} + +func (i initDict) InitializerName() string { + return sysModel.SysDictionary{}.TableName() +} + +func (i *initDict) InitializeData(ctx context.Context) (next context.Context, err error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + True := true + entities := []sysModel.SysDictionary{ + {Name: "性别", Type: "gender", Status: &True, Desc: "性别字典"}, + {Name: "数据库int类型", Type: "int", Status: &True, Desc: "int类型对应的数据库类型"}, + {Name: "数据库时间日期类型", Type: "time.Time", Status: &True, Desc: "数据库时间日期类型"}, + {Name: "数据库浮点型", Type: "float64", Status: &True, Desc: "数据库浮点型"}, + {Name: "数据库字符串", Type: "string", Status: &True, Desc: "数据库字符串"}, + {Name: "数据库bool类型", Type: "bool", Status: &True, Desc: "数据库bool类型"}, + } + + if err = db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, sysModel.SysDictionary{}.TableName()+"表数据初始化失败!") + } + next = context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initDict) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.Where("type = ?", "bool").First(&sysModel.SysDictionary{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/dictionary_detail.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/dictionary_detail.go new file mode 100644 index 000000000..3dea8b70f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/dictionary_detail.go @@ -0,0 +1,121 @@ +package system + +import ( + "context" + "fmt" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderDictDetail = initOrderDict + 1 + +type initDictDetail struct{} + +// auto run +func init() { + system.RegisterInit(initOrderDictDetail, &initDictDetail{}) +} + +func (i *initDictDetail) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysDictionaryDetail{}) +} + +func (i *initDictDetail) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysDictionaryDetail{}) +} + +func (i initDictDetail) InitializerName() string { + return sysModel.SysDictionaryDetail{}.TableName() +} + +func (i *initDictDetail) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + dicts, ok := ctx.Value(initDict{}.InitializerName()).([]sysModel.SysDictionary) + if !ok { + return ctx, errors.Wrap(system.ErrMissingDependentContext, + fmt.Sprintf("未找到 %s 表初始化数据", sysModel.SysDictionary{}.TableName())) + } + True := true + dicts[0].SysDictionaryDetails = []sysModel.SysDictionaryDetail{ + {Label: "男", Value: "1", Status: &True, Sort: 1}, + {Label: "女", Value: "2", Status: &True, Sort: 2}, + } + + dicts[1].SysDictionaryDetails = []sysModel.SysDictionaryDetail{ + {Label: "smallint", Value: "1", Status: &True, Extend: "mysql", Sort: 1}, + {Label: "mediumint", Value: "2", Status: &True, Extend: "mysql", Sort: 2}, + {Label: "int", Value: "3", Status: &True, Extend: "mysql", Sort: 3}, + {Label: "bigint", Value: "4", Status: &True, Extend: "mysql", Sort: 4}, + {Label: "int2", Value: "5", Status: &True, Extend: "pgsql", Sort: 5}, + {Label: "int4", Value: "6", Status: &True, Extend: "pgsql", Sort: 6}, + {Label: "int6", Value: "7", Status: &True, Extend: "pgsql", Sort: 7}, + {Label: "int8", Value: "8", Status: &True, Extend: "pgsql", Sort: 8}, + } + + dicts[2].SysDictionaryDetails = []sysModel.SysDictionaryDetail{ + {Label: "date", Status: &True}, + {Label: "time", Value: "1", Status: &True, Extend: "mysql", Sort: 1}, + {Label: "year", Value: "2", Status: &True, Extend: "mysql", Sort: 2}, + {Label: "datetime", Value: "3", Status: &True, Extend: "mysql", Sort: 3}, + {Label: "timestamp", Value: "5", Status: &True, Extend: "mysql", Sort: 5}, + {Label: "timestamptz", Value: "6", Status: &True, Extend: "pgsql", Sort: 5}, + } + dicts[3].SysDictionaryDetails = []sysModel.SysDictionaryDetail{ + {Label: "float", Status: &True}, + {Label: "double", Value: "1", Status: &True, Extend: "mysql", Sort: 1}, + {Label: "decimal", Value: "2", Status: &True, Extend: "mysql", Sort: 2}, + {Label: "numeric", Value: "3", Status: &True, Extend: "pgsql", Sort: 3}, + {Label: "smallserial", Value: "4", Status: &True, Extend: "pgsql", Sort: 4}, + } + + dicts[4].SysDictionaryDetails = []sysModel.SysDictionaryDetail{ + {Label: "char", Status: &True}, + {Label: "varchar", Value: "1", Status: &True, Extend: "mysql", Sort: 1}, + {Label: "tinyblob", Value: "2", Status: &True, Extend: "mysql", Sort: 2}, + {Label: "tinytext", Value: "3", Status: &True, Extend: "mysql", Sort: 3}, + {Label: "text", Value: "4", Status: &True, Extend: "mysql", Sort: 4}, + {Label: "blob", Value: "5", Status: &True, Extend: "mysql", Sort: 5}, + {Label: "mediumblob", Value: "6", Status: &True, Extend: "mysql", Sort: 6}, + {Label: "mediumtext", Value: "7", Status: &True, Extend: "mysql", Sort: 7}, + {Label: "longblob", Value: "8", Status: &True, Extend: "mysql", Sort: 8}, + {Label: "longtext", Value: "9", Status: &True, Extend: "mysql", Sort: 9}, + } + + dicts[5].SysDictionaryDetails = []sysModel.SysDictionaryDetail{ + {Label: "tinyint", Value: "1", Extend: "mysql", Status: &True}, + {Label: "bool", Value: "2", Extend: "pgsql", Status: &True}, + } + for _, dict := range dicts { + if err := db.Model(&dict).Association("SysDictionaryDetails"). + Replace(dict.SysDictionaryDetails); err != nil { + return ctx, errors.Wrap(err, sysModel.SysDictionaryDetail{}.TableName()+"表数据初始化失败!") + } + } + return ctx, nil +} + +func (i *initDictDetail) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + var dict sysModel.SysDictionary + if err := db.Preload("SysDictionaryDetails"). + First(&dict, &sysModel.SysDictionary{Name: "数据库bool类型"}).Error; err != nil { + return false + } + return len(dict.SysDictionaryDetails) > 0 && dict.SysDictionaryDetails[0].Label == "tinyint" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/excel_template.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/excel_template.go new file mode 100644 index 000000000..00f3ed5f1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/excel_template.go @@ -0,0 +1,75 @@ +package system + +import ( + "context" + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type initExcelTemplate struct{} + +const initOrderExcelTemplate = initOrderDictDetail + 1 + +// auto run +func init() { + system.RegisterInit(initOrderExcelTemplate, &initExcelTemplate{}) +} + +func (i initExcelTemplate) InitializerName() string { + return "sys_export_templates" +} + +func (i *initExcelTemplate) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysExportTemplate{}) +} + +func (i *initExcelTemplate) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysExportTemplate{}) +} + +func (i *initExcelTemplate) InitializeData(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + + entities := []sysModel.SysExportTemplate{ + { + Name: "api", + TableName: "sys_apis", + TemplateID: "api", + TemplateInfo: `{ +"path":"路径", +"method":"方法(大写)", +"description":"方法介绍", +"api_group":"方法分组" +}`, + }, + } + if err := db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, "sys_export_templates"+"表数据初始化失败!") + } + next := context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initExcelTemplate) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.First(&sysModel.SysExportTemplate{}).Error, gorm.ErrRecordNotFound) { + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/menu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/menu.go new file mode 100644 index 000000000..28a918758 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/menu.go @@ -0,0 +1,102 @@ +package system + +import ( + "context" + + . "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderMenu = initOrderAuthority + 1 + +type initMenu struct{} + +// auto run +func init() { + system.RegisterInit(initOrderMenu, &initMenu{}) +} + +func (i initMenu) InitializerName() string { + return SysBaseMenu{}.TableName() +} + +func (i *initMenu) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate( + &SysBaseMenu{}, + &SysBaseMenuParameter{}, + &SysBaseMenuBtn{}, + ) +} + +func (i *initMenu) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + m := db.Migrator() + return m.HasTable(&SysBaseMenu{}) && + m.HasTable(&SysBaseMenuParameter{}) && + m.HasTable(&SysBaseMenuBtn{}) +} + +func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, err error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + entities := []SysBaseMenu{ + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "odometer"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 9, Meta: Meta{Title: "关于我们", Icon: "info-filled"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}}, + {MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "example", Name: "example", Component: "view/example/index.vue", Sort: 7, Meta: Meta{Title: "示例文件", Icon: "management"}}, + {MenuLevel: 0, Hidden: false, ParentId: 11, Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 5, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}}, + {MenuLevel: 0, Hidden: false, ParentId: 11, Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}}, + {MenuLevel: 0, Hidden: false, ParentId: 11, Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: Meta{Title: "客户列表(资源示例)", Icon: "avatar"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "系统工具", Icon: "tools"}}, + {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoCode", Name: "autoCode", Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: Meta{Title: "代码生成器", Icon: "cpu", KeepAlive: true}}, + {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 3, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}}, + {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 4, Meta: Meta{Title: "系统配置", Icon: "operation"}}, + {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 2, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}}, + {MenuLevel: 0, Hidden: true, ParentId: 15, Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}}, + {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "模板配置", Icon: "folder"}}, + {MenuLevel: 0, Hidden: true, ParentId: 0, Path: "https://github.com/2024hufu", Name: "https://github.com/2024hufu", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}}, + {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}}, + {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}}, + {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "pubPlug", Name: "pubPlug", Component: "view/systemTools/pubPlug/pubPlug.vue", Sort: 3, Meta: Meta{Title: "打包插件", Icon: "files"}}, + {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}}, + {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 5, Meta: Meta{Title: "表格模板", Icon: "reading"}}, + {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "anInfo", Name: "anInfo", Component: "plugin/announcement/view/info.vue", Sort: 5, Meta: Meta{Title: "公告管理[示例]", Icon: "scaleToOriginal"}}, + {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "sysParams", Name: "sysParams", Component: "view/superAdmin/params/sysParams.vue", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "compass"}}, + } + if err = db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"表数据初始化失败!") + } + next = context.WithValue(ctx, i.InitializerName(), entities) + return next, nil +} + +func (i *initMenu) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + if errors.Is(db.Where("path = ?", "autoPkg").First(&SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 + return false + } + return true +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/user.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/user.go new file mode 100644 index 000000000..ea2817263 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/source/system/user.go @@ -0,0 +1,107 @@ +package system + +import ( + "context" + + sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/service/system" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gofrs/uuid/v5" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +const initOrderUser = initOrderAuthority + 1 + +type initUser struct{} + +// auto run +func init() { + system.RegisterInit(initOrderUser, &initUser{}) +} + +func (i *initUser) MigrateTable(ctx context.Context) (context.Context, error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + return ctx, db.AutoMigrate(&sysModel.SysUser{}) +} + +func (i *initUser) TableCreated(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + return db.Migrator().HasTable(&sysModel.SysUser{}) +} + +func (i initUser) InitializerName() string { + return sysModel.SysUser{}.TableName() +} + +func (i *initUser) InitializeData(ctx context.Context) (next context.Context, err error) { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return ctx, system.ErrMissingDBContext + } + + ap := ctx.Value("adminPassword") + apStr, ok := ap.(string) + if !ok { + apStr = "123456" + } + + password := utils.BcryptHash(apStr) + adminPassword := utils.BcryptHash(apStr) + + entities := []sysModel.SysUser{ + { + UUID: uuid.Must(uuid.NewV4()), + Username: "admin", + Password: adminPassword, + NickName: "Mr.奇淼", + HeaderImg: "https://qmplusimg.henrongyi.top/gva_header.jpg", + AuthorityId: 888, + Phone: "17611111111", + Email: "333333333@qq.com", + }, + { + UUID: uuid.Must(uuid.NewV4()), + Username: "a303176530", + Password: password, + NickName: "用户1", + HeaderImg: "https:///qmplusimg.henrongyi.top/1572075907logo.png", + AuthorityId: 9528, + Phone: "17611111111", + Email: "333333333@qq.com"}, + } + if err = db.Create(&entities).Error; err != nil { + return ctx, errors.Wrap(err, sysModel.SysUser{}.TableName()+"表数据初始化失败!") + } + next = context.WithValue(ctx, i.InitializerName(), entities) + authorityEntities, ok := ctx.Value(initAuthority{}.InitializerName()).([]sysModel.SysAuthority) + if !ok { + return next, errors.Wrap(system.ErrMissingDependentContext, "创建 [用户-权限] 关联失败, 未找到权限表初始化数据") + } + if err = db.Model(&entities[0]).Association("Authorities").Replace(authorityEntities); err != nil { + return next, err + } + if err = db.Model(&entities[1]).Association("Authorities").Replace(authorityEntities[:1]); err != nil { + return next, err + } + return next, err +} + +func (i *initUser) DataInserted(ctx context.Context) bool { + db, ok := ctx.Value("db").(*gorm.DB) + if !ok { + return false + } + var record sysModel.SysUser + if errors.Is(db.Where("username = ?", "a303176530"). + Preload("Authorities").First(&record).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 + return false + } + return len(record.Authorities) > 0 && record.Authorities[0].AuthorityId == 888 +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/task/clearTable.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/task/clearTable.go new file mode 100644 index 000000000..8d1e2f2f6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/task/clearTable.go @@ -0,0 +1,51 @@ +package task + +import ( + "errors" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/model/common" + "time" + + "gorm.io/gorm" +) + +//@author: [songzhibin97](https://github.com/songzhibin97) +//@function: ClearTable +//@description: 清理数据库表数据 +//@param: db(数据库对象) *gorm.DB, tableName(表名) string, compareField(比较字段) string, interval(间隔) string +//@return: error + +func ClearTable(db *gorm.DB) error { + var ClearTableDetail []common.ClearDB + + ClearTableDetail = append(ClearTableDetail, common.ClearDB{ + TableName: "sys_operation_records", + CompareField: "created_at", + Interval: "2160h", + }) + + ClearTableDetail = append(ClearTableDetail, common.ClearDB{ + TableName: "jwt_blacklists", + CompareField: "created_at", + Interval: "168h", + }) + + if db == nil { + return errors.New("db Cannot be empty") + } + + for _, detail := range ClearTableDetail { + duration, err := time.ParseDuration(detail.Interval) + if err != nil { + return err + } + if duration < 0 { + return errors.New("parse duration < 0") + } + err = db.Debug().Exec(fmt.Sprintf("DELETE FROM %s WHERE %s < ?", detail.TableName, detail.CompareField), time.Now().Add(-duration)).Error + if err != nil { + return err + } + } + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast.go new file mode 100644 index 000000000..b6f85d6d0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast.go @@ -0,0 +1,231 @@ +package ast + +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "go/ast" + "go/parser" + "go/token" + "log" +) + +// AddImport 增加 import 方法 +func AddImport(astNode ast.Node, imp string) { + impStr := fmt.Sprintf("\"%s\"", imp) + ast.Inspect(astNode, func(node ast.Node) bool { + if genDecl, ok := node.(*ast.GenDecl); ok { + if genDecl.Tok == token.IMPORT { + for i := range genDecl.Specs { + if impNode, ok := genDecl.Specs[i].(*ast.ImportSpec); ok { + if impNode.Path.Value == impStr { + return false + } + } + } + genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: impStr, + }, + }) + } + } + return true + }) +} + +// FindFunction 查询特定function方法 +func FindFunction(astNode ast.Node, FunctionName string) *ast.FuncDecl { + var funcDeclP *ast.FuncDecl + ast.Inspect(astNode, func(node ast.Node) bool { + if funcDecl, ok := node.(*ast.FuncDecl); ok { + if funcDecl.Name.String() == FunctionName { + funcDeclP = funcDecl + return false + } + } + return true + }) + return funcDeclP +} + +// FindArray 查询特定数组方法 +func FindArray(astNode ast.Node, identName, selectorExprName string) *ast.CompositeLit { + var assignStmt *ast.CompositeLit + ast.Inspect(astNode, func(n ast.Node) bool { + switch node := n.(type) { + case *ast.AssignStmt: + for _, expr := range node.Rhs { + if exprType, ok := expr.(*ast.CompositeLit); ok { + if arrayType, ok := exprType.Type.(*ast.ArrayType); ok { + sel, ok1 := arrayType.Elt.(*ast.SelectorExpr) + x, ok2 := sel.X.(*ast.Ident) + if ok1 && ok2 && x.Name == identName && sel.Sel.Name == selectorExprName { + assignStmt = exprType + return false + } + } + } + } + } + return true + }) + return assignStmt +} + +func CreateMenuStructAst(menus []system.SysBaseMenu) *[]ast.Expr { + var menuElts []ast.Expr + for i := range menus { + elts := []ast.Expr{ // 结构体的字段 + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "ParentId"}, + Value: &ast.BasicLit{Kind: token.INT, Value: "0"}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Path"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Path)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Name"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Name)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Hidden"}, + Value: &ast.Ident{Name: "false"}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Component"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Component)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Sort"}, + Value: &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%d", menus[i].Sort)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Meta"}, + Value: &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: &ast.Ident{Name: "model"}, + Sel: &ast.Ident{Name: "Meta"}, + }, + Elts: []ast.Expr{ + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Title"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Title)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Icon"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Icon)}, + }, + }, + }, + }, + } + menuElts = append(menuElts, &ast.CompositeLit{ + Type: nil, + Elts: elts, + }) + } + return &menuElts +} + +func CreateApiStructAst(apis []system.SysApi) *[]ast.Expr { + var apiElts []ast.Expr + for i := range apis { + elts := []ast.Expr{ // 结构体的字段 + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Path"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Path)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Description"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Description)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "ApiGroup"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].ApiGroup)}, + }, + &ast.KeyValueExpr{ + Key: &ast.Ident{Name: "Method"}, + Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Method)}, + }, + } + apiElts = append(apiElts, &ast.CompositeLit{ + Type: nil, + Elts: elts, + }) + } + return &apiElts +} + +// 检查是否存在Import +func CheckImport(file *ast.File, importPath string) bool { + for _, imp := range file.Imports { + // Remove quotes around the import path + path := imp.Path.Value[1 : len(imp.Path.Value)-1] + + if path == importPath { + return true + } + } + + return false +} + +func clearPosition(astNode ast.Node) { + ast.Inspect(astNode, func(n ast.Node) bool { + switch node := n.(type) { + case *ast.Ident: + // 清除位置信息 + node.NamePos = token.NoPos + case *ast.CallExpr: + // 清除位置信息 + node.Lparen = token.NoPos + node.Rparen = token.NoPos + case *ast.BasicLit: + // 清除位置信息 + node.ValuePos = token.NoPos + case *ast.SelectorExpr: + // 清除位置信息 + node.Sel.NamePos = token.NoPos + case *ast.BinaryExpr: + node.OpPos = token.NoPos + case *ast.UnaryExpr: + node.OpPos = token.NoPos + case *ast.StarExpr: + node.Star = token.NoPos + } + return true + }) +} + +func CreateStmt(statement string) *ast.ExprStmt { + expr, err := parser.ParseExpr(statement) + if err != nil { + log.Fatal(err) + } + clearPosition(expr) + return &ast.ExprStmt{X: expr} +} + +func IsBlockStmt(node ast.Node) bool { + _, ok := node.(*ast.BlockStmt) + return ok +} + +func VariableExistsInBlock(block *ast.BlockStmt, varName string) bool { + exists := false + ast.Inspect(block, func(n ast.Node) bool { + switch node := n.(type) { + case *ast.AssignStmt: + for _, expr := range node.Lhs { + if ident, ok := expr.(*ast.Ident); ok && ident.Name == varName { + exists = true + return false + } + } + } + return true + }) + return exists +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_auto_enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_auto_enter.go new file mode 100644 index 000000000..382f554e8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_auto_enter.go @@ -0,0 +1,47 @@ +package ast + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" +) + +func ImportForAutoEnter(path string, funcName string, code string) { + src, err := os.ReadFile(path) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + ast.Inspect(astFile, func(node ast.Node) bool { + if typeSpec, ok := node.(*ast.TypeSpec); ok { + if typeSpec.Name.Name == funcName { + if st, ok := typeSpec.Type.(*ast.StructType); ok { + for i := range st.Fields.List { + if t, ok := st.Fields.List[i].Type.(*ast.Ident); ok { + if t.Name == code { + return false + } + } + } + sn := &ast.Field{ + Type: &ast.Ident{Name: code}, + } + st.Fields.List = append(st.Fields.List, sn) + } + } + } + return true + }) + var out []byte + bf := bytes.NewBuffer(out) + err = printer.Fprint(bf, fileSet, astFile) + if err != nil { + return + } + _ = os.WriteFile(path, bf.Bytes(), 0666) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_enter.go new file mode 100644 index 000000000..7a5c72745 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_enter.go @@ -0,0 +1,181 @@ +package ast + +import ( + "bytes" + "go/ast" + "go/format" + "go/parser" + "go/token" + "golang.org/x/text/cases" + "golang.org/x/text/language" + "log" + "os" + "strconv" + "strings" +) + +type Visitor struct { + ImportCode string + StructName string + PackageName string + GroupName string +} + +func (vi *Visitor) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.GenDecl: + // 查找有没有import context包 + // Notice:没有考虑没有import任何包的情况 + if n.Tok == token.IMPORT && vi.ImportCode != "" { + vi.addImport(n) + // 不需要再遍历子树 + return nil + } + if n.Tok == token.TYPE && vi.StructName != "" && vi.PackageName != "" && vi.GroupName != "" { + vi.addStruct(n) + return nil + } + case *ast.FuncDecl: + if n.Name.Name == "Routers" { + vi.addFuncBodyVar(n) + return nil + } + + } + return vi +} + +func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor { + for i := range genDecl.Specs { + switch n := genDecl.Specs[i].(type) { + case *ast.TypeSpec: + if strings.Index(n.Name.Name, "Group") > -1 { + switch t := n.Type.(type) { + case *ast.StructType: + f := &ast.Field{ + Names: []*ast.Ident{ + { + Name: vi.StructName, + Obj: &ast.Object{ + Kind: ast.Var, + Name: vi.StructName, + }, + }, + }, + Type: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: vi.PackageName, + }, + Sel: &ast.Ident{ + Name: vi.GroupName, + }, + }, + } + t.Fields.List = append(t.Fields.List, f) + } + } + } + } + return vi +} + +func (vi *Visitor) addImport(genDecl *ast.GenDecl) ast.Visitor { + // 是否已经import + hasImported := false + for _, v := range genDecl.Specs { + importSpec := v.(*ast.ImportSpec) + // 如果已经包含 + if importSpec.Path.Value == strconv.Quote(vi.ImportCode) { + hasImported = true + } + } + if !hasImported { + genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(vi.ImportCode), + }, + }) + } + return vi +} + +func (vi *Visitor) addFuncBodyVar(funDecl *ast.FuncDecl) ast.Visitor { + hasVar := false + for _, v := range funDecl.Body.List { + switch varSpec := v.(type) { + case *ast.AssignStmt: + for i := range varSpec.Lhs { + switch nn := varSpec.Lhs[i].(type) { + case *ast.Ident: + if nn.Name == vi.PackageName+"Router" { + hasVar = true + } + } + } + } + } + if !hasVar { + assignStmt := &ast.AssignStmt{ + Lhs: []ast.Expr{ + &ast.Ident{ + Name: vi.PackageName + "Router", + Obj: &ast.Object{ + Kind: ast.Var, + Name: vi.PackageName + "Router", + }, + }, + }, + Tok: token.DEFINE, + Rhs: []ast.Expr{ + &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: "router", + }, + Sel: &ast.Ident{ + Name: "RouterGroupApp", + }, + }, + Sel: &ast.Ident{ + Name: cases.Title(language.English).String(vi.PackageName), + }, + }, + }, + } + funDecl.Body.List = append(funDecl.Body.List, funDecl.Body.List[1]) + index := 1 + copy(funDecl.Body.List[index+1:], funDecl.Body.List[index:]) + funDecl.Body.List[index] = assignStmt + } + return vi +} + +func ImportReference(filepath, importCode, structName, packageName, groupName string) error { + fSet := token.NewFileSet() + fParser, err := parser.ParseFile(fSet, filepath, nil, parser.ParseComments) + if err != nil { + return err + } + importCode = strings.TrimSpace(importCode) + v := &Visitor{ + ImportCode: importCode, + StructName: structName, + PackageName: packageName, + GroupName: groupName, + } + if importCode == "" { + ast.Print(fSet, fParser) + } + + ast.Walk(v, fParser) + + var output []byte + buffer := bytes.NewBuffer(output) + err = format.Node(buffer, fSet, fParser) + if err != nil { + log.Fatal(err) + } + // 写回数据 + return os.WriteFile(filepath, buffer.Bytes(), 0o600) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_gorm.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_gorm.go new file mode 100644 index 000000000..d9c1beb3e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_gorm.go @@ -0,0 +1,166 @@ +package ast + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" +) + +// 自动为 gorm.go 注册一个自动迁移 +func AddRegisterTablesAst(path, funcName, pk, varName, dbName, model string) { + modelPk := fmt.Sprintf("github.com/flipped-aurora/gin-vue-admin/server/model/%s", pk) + src, err := os.ReadFile(path) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + if err != nil { + fmt.Println(err) + } + AddImport(astFile, modelPk) + FuncNode := FindFunction(astFile, funcName) + if FuncNode != nil { + ast.Print(fileSet, FuncNode) + } + addDBVar(FuncNode.Body, varName, dbName) + addAutoMigrate(FuncNode.Body, varName, pk, model) + var out []byte + bf := bytes.NewBuffer(out) + printer.Fprint(bf, fileSet, astFile) + + os.WriteFile(path, bf.Bytes(), 0666) +} + +// 增加一个 db库变量 +func addDBVar(astBody *ast.BlockStmt, varName, dbName string) { + if dbName == "" { + return + } + dbStr := fmt.Sprintf("\"%s\"", dbName) + + for i := range astBody.List { + if assignStmt, ok := astBody.List[i].(*ast.AssignStmt); ok { + if ident, ok := assignStmt.Lhs[0].(*ast.Ident); ok { + if ident.Name == varName { + return + } + } + } + } + assignNode := &ast.AssignStmt{ + Lhs: []ast.Expr{ + &ast.Ident{ + Name: varName, + }, + }, + Tok: token.DEFINE, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: "global", + }, + Sel: &ast.Ident{ + Name: "GetGlobalDBByDBName", + }, + }, + Args: []ast.Expr{ + &ast.BasicLit{ + Kind: token.STRING, + Value: dbStr, + }, + }, + }, + }, + } + astBody.List = append([]ast.Stmt{assignNode}, astBody.List...) +} + +// 为db库变量增加 AutoMigrate 方法 +func addAutoMigrate(astBody *ast.BlockStmt, dbname string, pk string, model string) { + if dbname == "" { + dbname = "db" + } + flag := true + ast.Inspect(astBody, func(node ast.Node) bool { + // 首先判断需要加入的方法调用语句是否存在 不存在则直接走到下方逻辑 + switch n := node.(type) { + case *ast.CallExpr: + // 判断是否找到了AutoMigrate语句 + if s, ok := n.Fun.(*ast.SelectorExpr); ok { + if x, ok := s.X.(*ast.Ident); ok { + if s.Sel.Name == "AutoMigrate" && x.Name == dbname { + flag = false + if !NeedAppendModel(n, pk, model) { + return false + } + // 判断已经找到了AutoMigrate语句 + n.Args = append(n.Args, &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: pk, + }, + Sel: &ast.Ident{ + Name: model, + }, + }, + }) + return false + } + } + } + } + return true + //然后判断 pk.model是否存在 如果存在直接跳出 如果不存在 则向已经找到的方法调用语句的node里面push一条 + }) + + if flag { + exprStmt := &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: dbname, + }, + Sel: &ast.Ident{ + Name: "AutoMigrate", + }, + }, + Args: []ast.Expr{ + &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: pk, + }, + Sel: &ast.Ident{ + Name: model, + }, + }, + }, + }, + }} + astBody.List = append(astBody.List, exprStmt) + } +} + +// 为automigrate增加实参 +func NeedAppendModel(callNode ast.Node, pk string, model string) bool { + flag := true + ast.Inspect(callNode, func(node ast.Node) bool { + switch n := node.(type) { + case *ast.SelectorExpr: + if x, ok := n.X.(*ast.Ident); ok { + if n.Sel.Name == model && x.Name == pk { + flag = false + return false + } + } + } + return true + }) + return flag +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_init_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_init_test.go new file mode 100644 index 000000000..ec6bd9b5e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_init_test.go @@ -0,0 +1,11 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" +) + +func init() { + global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("../../../") + global.GVA_CONFIG.AutoCode.Server = "server" +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_rollback.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_rollback.go new file mode 100644 index 000000000..daa84226f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_rollback.go @@ -0,0 +1,173 @@ +package ast + +import ( + "bytes" + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" + "path/filepath" +) + +func RollBackAst(pk, model string) { + RollGormBack(pk, model) + RollRouterBack(pk, model) +} + +func RollGormBack(pk, model string) { + + // 首先分析存在多少个ttt作为调用方的node块 + // 如果多个 仅仅删除对应块即可 + // 如果单个 那么还需要剔除import + path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go") + src, err := os.ReadFile(path) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + if err != nil { + fmt.Println(err) + } + var n *ast.CallExpr + var k int = -1 + var pkNum = 0 + ast.Inspect(astFile, func(node ast.Node) bool { + if node, ok := node.(*ast.CallExpr); ok { + for i := range node.Args { + pkOK := false + modelOK := false + ast.Inspect(node.Args[i], func(item ast.Node) bool { + if ii, ok := item.(*ast.Ident); ok { + if ii.Name == pk { + pkOK = true + pkNum++ + } + if ii.Name == model { + modelOK = true + } + } + if pkOK && modelOK { + n = node + k = i + } + return true + }) + } + } + return true + }) + if k > -1 { + n.Args = append(append([]ast.Expr{}, n.Args[:k]...), n.Args[k+1:]...) + } + if pkNum == 1 { + var imI int = -1 + var gp *ast.GenDecl + ast.Inspect(astFile, func(node ast.Node) bool { + if gen, ok := node.(*ast.GenDecl); ok { + for i := range gen.Specs { + if imspec, ok := gen.Specs[i].(*ast.ImportSpec); ok { + if imspec.Path.Value == "\"github.com/flipped-aurora/gin-vue-admin/server/model/"+pk+"\"" { + gp = gen + imI = i + return false + } + } + } + } + return true + }) + + if imI > -1 { + gp.Specs = append(append([]ast.Spec{}, gp.Specs[:imI]...), gp.Specs[imI+1:]...) + } + } + + var out []byte + bf := bytes.NewBuffer(out) + printer.Fprint(bf, fileSet, astFile) + os.Remove(path) + os.WriteFile(path, bf.Bytes(), 0666) + +} + +func RollRouterBack(pk, model string) { + + // 首先抓到所有的代码块结构 {} + // 分析结构中是否存在一个变量叫做 pk+Router + // 然后获取到代码块指针 对内部需要回滚的代码进行剔除 + path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go") + src, err := os.ReadFile(path) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, 0) + if err != nil { + fmt.Println(err) + } + + var block *ast.BlockStmt + var routerStmt *ast.FuncDecl + + ast.Inspect(astFile, func(node ast.Node) bool { + if n, ok := node.(*ast.FuncDecl); ok { + if n.Name.Name == "initBizRouter" { + routerStmt = n + } + } + + if n, ok := node.(*ast.BlockStmt); ok { + ast.Inspect(n, func(bNode ast.Node) bool { + if in, ok := bNode.(*ast.Ident); ok { + if in.Name == pk+"Router" { + block = n + return false + } + } + return true + }) + return true + } + return true + }) + var k int + for i := range block.List { + if stmtNode, ok := block.List[i].(*ast.ExprStmt); ok { + ast.Inspect(stmtNode, func(node ast.Node) bool { + if n, ok := node.(*ast.Ident); ok { + if n.Name == "Init"+model+"Router" { + k = i + return false + } + } + return true + }) + } + } + + block.List = append(append([]ast.Stmt{}, block.List[:k]...), block.List[k+1:]...) + + if len(block.List) == 1 { + // 说明这个块就没任何意义了 + block.List = nil + } + + for i, n := range routerStmt.Body.List { + if n, ok := n.(*ast.BlockStmt); ok { + if n.List == nil { + routerStmt.Body.List = append(append([]ast.Stmt{}, routerStmt.Body.List[:i]...), routerStmt.Body.List[i+1:]...) + i-- + } + } + } + + var out []byte + bf := bytes.NewBuffer(out) + printer.Fprint(bf, fileSet, astFile) + os.Remove(path) + os.WriteFile(path, bf.Bytes(), 0666) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_router.go new file mode 100644 index 000000000..86356b819 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_router.go @@ -0,0 +1,135 @@ +package ast + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" + "strings" +) + +func AppendNodeToList(stmts []ast.Stmt, stmt ast.Stmt, index int) []ast.Stmt { + return append(stmts[:index], append([]ast.Stmt{stmt}, stmts[index:]...)...) +} + +func AddRouterCode(path, funcName, pk, model string) { + src, err := os.ReadFile(path) + if err != nil { + fmt.Println(err) + } + fileSet := token.NewFileSet() + astFile, err := parser.ParseFile(fileSet, "", src, parser.ParseComments) + + if err != nil { + fmt.Println(err) + } + + FuncNode := FindFunction(astFile, funcName) + + pkName := strings.ToUpper(pk[:1]) + pk[1:] + routerName := fmt.Sprintf("%sRouter", pk) + modelName := fmt.Sprintf("Init%sRouter", model) + var bloctPre *ast.BlockStmt + for i := len(FuncNode.Body.List) - 1; i >= 0; i-- { + if block, ok := FuncNode.Body.List[i].(*ast.BlockStmt); ok { + bloctPre = block + } + } + ast.Print(fileSet, FuncNode) + if ok, b := needAppendRouter(FuncNode, pk); ok { + routerNode := + &ast.BlockStmt{ + List: []ast.Stmt{ + &ast.AssignStmt{ + Lhs: []ast.Expr{ + &ast.Ident{Name: routerName}, + }, + Tok: token.DEFINE, + Rhs: []ast.Expr{ + &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: "router"}, + Sel: &ast.Ident{Name: "RouterGroupApp"}, + }, + Sel: &ast.Ident{Name: pkName}, + }, + }, + }, + }, + } + + FuncNode.Body.List = AppendNodeToList(FuncNode.Body.List, routerNode, len(FuncNode.Body.List)-1) + bloctPre = routerNode + } else { + bloctPre = b + } + + if needAppendInit(FuncNode, routerName, modelName) { + bloctPre.List = append(bloctPre.List, + &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{Name: routerName}, + Sel: &ast.Ident{Name: modelName}, + }, + Args: []ast.Expr{ + &ast.Ident{ + Name: "privateGroup", + }, + &ast.Ident{ + Name: "publicGroup", + }, + }, + }, + }) + } + var out []byte + bf := bytes.NewBuffer(out) + printer.Fprint(bf, fileSet, astFile) + os.WriteFile(path, bf.Bytes(), 0666) +} + +func needAppendRouter(funcNode ast.Node, pk string) (bool, *ast.BlockStmt) { + flag := true + var block *ast.BlockStmt + ast.Inspect(funcNode, func(node ast.Node) bool { + switch n := node.(type) { + case *ast.BlockStmt: + for i := range n.List { + if assignNode, ok := n.List[i].(*ast.AssignStmt); ok { + if identNode, ok := assignNode.Lhs[0].(*ast.Ident); ok { + if identNode.Name == fmt.Sprintf("%sRouter", pk) { + flag = false + block = n + return false + } + } + } + } + + } + return true + }) + return flag, block +} + +func needAppendInit(funcNode ast.Node, routerName string, modelName string) bool { + flag := true + ast.Inspect(funcNode, func(node ast.Node) bool { + switch n := funcNode.(type) { + case *ast.CallExpr: + if selectNode, ok := n.Fun.(*ast.SelectorExpr); ok { + x, xok := selectNode.X.(*ast.Ident) + if xok && x.Name == routerName && selectNode.Sel.Name == modelName { + flag = false + return false + } + } + } + return true + }) + return flag +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_test.go new file mode 100644 index 000000000..001f530f9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_test.go @@ -0,0 +1,32 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" + "path/filepath" + "testing" +) + +func TestAst(t *testing.T) { + filename := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "plugin.go") + fileSet := token.NewFileSet() + file, err := parser.ParseFile(fileSet, filename, nil, parser.ParseComments) + if err != nil { + t.Error(err) + return + } + err = ast.Print(fileSet, file) + if err != nil { + t.Error(err) + return + } + err = printer.Fprint(os.Stdout, token.NewFileSet(), file) + if err != nil { + panic(err) + } + +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_type.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_type.go new file mode 100644 index 000000000..c4e905eda --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/ast_type.go @@ -0,0 +1,53 @@ +package ast + +type Type string + +func (r Type) String() string { + return string(r) +} + +func (r Type) Group() string { + switch r { + case TypePackageApiEnter: + return "ApiGroup" + case TypePackageRouterEnter: + return "RouterGroup" + case TypePackageServiceEnter: + return "ServiceGroup" + case TypePackageApiModuleEnter: + return "ApiGroup" + case TypePackageRouterModuleEnter: + return "RouterGroup" + case TypePackageServiceModuleEnter: + return "ServiceGroup" + case TypePluginApiEnter: + return "api" + case TypePluginRouterEnter: + return "router" + case TypePluginServiceEnter: + return "service" + default: + return "" + } +} + +const ( + TypePackageApiEnter = "PackageApiEnter" // server/api/v1/enter.go + TypePackageRouterEnter = "PackageRouterEnter" // server/router/enter.go + TypePackageServiceEnter = "PackageServiceEnter" // server/service/enter.go + TypePackageApiModuleEnter = "PackageApiModuleEnter" // server/api/v1/{package}/enter.go + TypePackageRouterModuleEnter = "PackageRouterModuleEnter" // server/router/{package}/enter.go + TypePackageServiceModuleEnter = "PackageServiceModuleEnter" // server/service/{package}/enter.go + TypePackageInitializeGorm = "PackageInitializeGorm" // server/initialize/gorm_biz.go + TypePackageInitializeRouter = "PackageInitializeRouter" // server/initialize/router_biz.go + TypePluginGen = "PluginGen" // server/plugin/{package}/gen/main.go + TypePluginApiEnter = "PluginApiEnter" // server/plugin/{package}/enter.go + TypePluginInitializeV1 = "PluginInitializeV1" // server/initialize/plugin_biz_v1.go + TypePluginInitializeV2 = "PluginInitializeV2" // server/initialize/plugin_biz_v2.go + TypePluginRouterEnter = "PluginRouterEnter" // server/plugin/{package}/enter.go + TypePluginServiceEnter = "PluginServiceEnter" // server/plugin/{package}/enter.go + TypePluginInitializeApi = "PluginInitializeApi" // server/plugin/{package}/initialize/api.go + TypePluginInitializeGorm = "PluginInitializeGorm" // server/plugin/{package}/initialize/gorm.go + TypePluginInitializeMenu = "PluginInitializeMenu" // server/plugin/{package}/initialize/menu.go + TypePluginInitializeRouter = "PluginInitializeRouter" // server/plugin/{package}/initialize/router.go +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/import.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/import.go new file mode 100644 index 000000000..5de18a317 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/import.go @@ -0,0 +1,94 @@ +package ast + +import ( + "go/ast" + "go/token" + "io" + "strings" +) + +type Import struct { + Base + ImportPath string // 导包路径 +} + +func NewImport(importPath string) *Import { + return &Import{ImportPath: importPath} +} + +func (a *Import) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + return a.Base.Parse(filename, writer) +} + +func (a *Import) Rollback(file *ast.File) error { + if a.ImportPath == "" { + return nil + } + for i := 0; i < len(file.Decls); i++ { + v1, o1 := file.Decls[i].(*ast.GenDecl) + if o1 { + if v1.Tok != token.IMPORT { + break + } + for j := 0; j < len(v1.Specs); j++ { + v2, o2 := v1.Specs[j].(*ast.ImportSpec) + if o2 && strings.HasSuffix(a.ImportPath, v2.Path.Value) { + v1.Specs = append(v1.Specs[:j], v1.Specs[j+1:]...) + if len(v1.Specs) == 0 { + file.Decls = append(file.Decls[:i], file.Decls[i+1:]...) + } // 如果没有import声明,就删除, 如果不删除则会出现import() + break + } + } + } + } + return nil +} + +func (a *Import) Injection(file *ast.File) error { + if a.ImportPath == "" { + return nil + } + var has bool + for i := 0; i < len(file.Decls); i++ { + v1, o1 := file.Decls[i].(*ast.GenDecl) + if o1 { + if v1.Tok != token.IMPORT { + break + } + for j := 0; j < len(v1.Specs); j++ { + v2, o2 := v1.Specs[j].(*ast.ImportSpec) + if o2 && strings.HasSuffix(a.ImportPath, v2.Path.Value) { + has = true + break + } + } + if !has { + spec := &ast.ImportSpec{ + Path: &ast.BasicLit{Kind: token.STRING, Value: a.ImportPath}, + } + v1.Specs = append(v1.Specs, spec) + return nil + } + } + } + if !has { + decls := file.Decls + file.Decls = make([]ast.Decl, 0, len(file.Decls)+1) + decl := &ast.GenDecl{ + Tok: token.IMPORT, + Specs: []ast.Spec{ + &ast.ImportSpec{ + Path: &ast.BasicLit{Kind: token.STRING, Value: a.ImportPath}, + }, + }, + } + file.Decls = append(file.Decls, decl) + file.Decls = append(file.Decls, decls...) + } // 如果没有import声明,就创建一个, 主要要放在第一个 + return nil +} + +func (a *Import) Format(filename string, writer io.Writer, file *ast.File) error { + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/interfaces.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/interfaces.go new file mode 100644 index 000000000..33ecc4723 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/interfaces.go @@ -0,0 +1,17 @@ +package ast + +import ( + "go/ast" + "io" +) + +type Ast interface { + // Parse 解析文件/代码 + Parse(filename string, writer io.Writer) (file *ast.File, err error) + // Rollback 回滚 + Rollback(file *ast.File) error + // Injection 注入 + Injection(file *ast.File) error + // Format 格式化输出 + Format(filename string, writer io.Writer, file *ast.File) error +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/interfaces_base.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/interfaces_base.go new file mode 100644 index 000000000..05cc7f779 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/interfaces_base.go @@ -0,0 +1,76 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/pkg/errors" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io" + "os" + "path" + "path/filepath" + "strings" +) + +type Base struct{} + +func (a *Base) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + fileSet := token.NewFileSet() + if writer != nil { + file, err = parser.ParseFile(fileSet, filename, nil, parser.ParseComments) + } else { + file, err = parser.ParseFile(fileSet, filename, writer, parser.ParseComments) + } + if err != nil { + return nil, errors.Wrapf(err, "[filepath:%s]打开/解析文件失败!", filename) + } + return file, nil +} + +func (a *Base) Rollback(file *ast.File) error { + return nil +} + +func (a *Base) Injection(file *ast.File) error { + return nil +} + +func (a *Base) Format(filename string, writer io.Writer, file *ast.File) error { + fileSet := token.NewFileSet() + if writer == nil { + open, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666) + defer open.Close() + if err != nil { + return errors.Wrapf(err, "[filepath:%s]打开文件失败!", filename) + } + writer = open + } + err := format.Node(writer, fileSet, file) + if err != nil { + return errors.Wrapf(err, "[filepath:%s]注入失败!", filename) + } + return nil +} + +// RelativePath 绝对路径转相对路径 +func (a *Base) RelativePath(filePath string) string { + server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + hasServer := strings.Index(filePath, server) + if hasServer != -1 { + filePath = strings.TrimPrefix(filePath, server) + keys := strings.Split(filePath, string(filepath.Separator)) + filePath = path.Join(keys...) + } + return filePath +} + +// AbsolutePath 相对路径转绝对路径 +func (a *Base) AbsolutePath(filePath string) string { + server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server) + keys := strings.Split(filePath, "/") + filePath = filepath.Join(keys...) + filePath = filepath.Join(server, filePath) + return filePath +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_enter.go new file mode 100644 index 000000000..f4b6305f9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_enter.go @@ -0,0 +1,85 @@ +package ast + +import ( + "go/ast" + "go/token" + "io" +) + +// PackageEnter 模块化入口 +type PackageEnter struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + StructName string // 结构体名称 + PackageName string // 包名 + RelativePath string // 相对路径 + PackageStructName string // 包结构体名称 +} + +func (a *PackageEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PackageEnter) Rollback(file *ast.File) error { + // 无需回滚 + return nil +} + +func (a *PackageEnter) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + ast.Inspect(file, func(n ast.Node) bool { + genDecl, ok := n.(*ast.GenDecl) + if !ok || genDecl.Tok != token.TYPE { + return true + } + + for _, spec := range genDecl.Specs { + typeSpec, specok := spec.(*ast.TypeSpec) + if !specok || typeSpec.Name.Name != a.Type.Group() { + continue + } + + structType, structTypeOK := typeSpec.Type.(*ast.StructType) + if !structTypeOK { + continue + } + + for _, field := range structType.Fields.List { + if len(field.Names) == 1 && field.Names[0].Name == a.StructName { + return true + } + } + + field := &ast.Field{ + Names: []*ast.Ident{{Name: a.StructName}}, + Type: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.PackageStructName}, + }, + } + structType.Fields.List = append(structType.Fields.List, field) + return false + } + + return true + }) + return nil +} + +func (a *PackageEnter) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_enter_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_enter_test.go new file mode 100644 index 000000000..3cf4ab459 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_enter_test.go @@ -0,0 +1,154 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPackageEnter_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + PackageName string + PackageStructName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试ExampleApiGroup回滚", + fields: fields{ + Type: TypePackageApiEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"`, + StructName: "ExampleApiGroup", + PackageName: "example", + PackageStructName: "ApiGroup", + }, + wantErr: false, + }, + { + name: "测试ExampleRouterGroup回滚", + fields: fields{ + Type: TypePackageRouterEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router/example"`, + StructName: "Example", + PackageName: "example", + PackageStructName: "RouterGroup", + }, + wantErr: false, + }, + { + name: "测试ExampleServiceGroup回滚", + fields: fields{ + Type: TypePackageServiceEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service/example"`, + StructName: "ExampleServiceGroup", + PackageName: "example", + PackageStructName: "ServiceGroup", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageEnter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + PackageName: tt.fields.PackageName, + PackageStructName: tt.fields.PackageStructName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPackageEnter_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + PackageName string + PackageStructName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试ExampleApiGroup注入", + fields: fields{ + Type: TypePackageApiEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"`, + StructName: "ExampleApiGroup", + PackageName: "example", + PackageStructName: "ApiGroup", + }, + }, + { + name: "测试ExampleRouterGroup注入", + fields: fields{ + Type: TypePackageRouterEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router/example"`, + StructName: "Example", + PackageName: "example", + PackageStructName: "RouterGroup", + }, + wantErr: false, + }, + { + name: "测试ExampleServiceGroup注入", + fields: fields{ + Type: TypePackageServiceEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service/example"`, + StructName: "ExampleServiceGroup", + PackageName: "example", + PackageStructName: "ServiceGroup", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageEnter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + PackageName: tt.fields.PackageName, + PackageStructName: tt.fields.PackageStructName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Format() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_gorm.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_gorm.go new file mode 100644 index 000000000..594f71490 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_gorm.go @@ -0,0 +1,196 @@ +package ast + +import ( + "fmt" + "go/ast" + "go/token" + "io" +) + +// PackageInitializeGorm 包初始化gorm +type PackageInitializeGorm struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + Business string // 业务库 gva => gva, 不要传"gva" + StructName string // 结构体名称 + PackageName string // 包名 + RelativePath string // 相对路径 + IsNew bool // 是否使用new关键字 true: new(PackageName.StructName) false: &PackageName.StructName{} +} + +func (a *PackageInitializeGorm) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PackageInitializeGorm) Rollback(file *ast.File) error { + packageNameNum := 0 + // 寻找目标结构 + ast.Inspect(file, func(n ast.Node) bool { + // 总调用的db变量根据business来决定 + varDB := a.Business + "Db" + + if a.Business == "" { + varDB = "db" + } + + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + // 检查是不是 db.AutoMigrate() 方法 + selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok || selExpr.Sel.Name != "AutoMigrate" { + return true + } + + // 检查调用方是不是 db + ident, ok := selExpr.X.(*ast.Ident) + if !ok || ident.Name != varDB { + return true + } + + // 删除结构体参数 + for i := 0; i < len(callExpr.Args); i++ { + if com, comok := callExpr.Args[i].(*ast.CompositeLit); comok { + if selector, exprok := com.Type.(*ast.SelectorExpr); exprok { + if x, identok := selector.X.(*ast.Ident); identok { + if x.Name == a.PackageName { + packageNameNum++ + if selector.Sel.Name == a.StructName { + callExpr.Args = append(callExpr.Args[:i], callExpr.Args[i+1:]...) + i-- + } + } + } + } + } + } + return true + }) + + if packageNameNum == 1 { + _ = NewImport(a.ImportPath).Rollback(file) + } + return nil +} + +func (a *PackageInitializeGorm) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + bizModelDecl := FindFunction(file, "bizModel") + if bizModelDecl != nil { + a.addDbVar(bizModelDecl.Body) + } + // 寻找目标结构 + ast.Inspect(file, func(n ast.Node) bool { + // 总调用的db变量根据business来决定 + varDB := a.Business + "Db" + + if a.Business == "" { + varDB = "db" + } + + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + // 检查是不是 db.AutoMigrate() 方法 + selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok || selExpr.Sel.Name != "AutoMigrate" { + return true + } + + // 检查调用方是不是 db + ident, ok := selExpr.X.(*ast.Ident) + if !ok || ident.Name != varDB { + return true + } + + // 添加结构体参数 + callExpr.Args = append(callExpr.Args, &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: ast.NewIdent(a.PackageName), + Sel: ast.NewIdent(a.StructName), + }, + }) + return true + }) + return nil +} + +func (a *PackageInitializeGorm) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} + +// 创建businessDB变量 +func (a *PackageInitializeGorm) addDbVar(astBody *ast.BlockStmt) { + for i := range astBody.List { + if assignStmt, ok := astBody.List[i].(*ast.AssignStmt); ok { + if ident, ok := assignStmt.Lhs[0].(*ast.Ident); ok { + if (a.Business == "" && ident.Name == "db") || ident.Name == a.Business+"Db" { + return + } + } + } + } + + // 添加 businessDb := global.GetGlobalDBByDBName("business") 变量 + assignNode := &ast.AssignStmt{ + Lhs: []ast.Expr{ + &ast.Ident{ + Name: a.Business + "Db", + }, + }, + Tok: token.DEFINE, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: "global", + }, + Sel: &ast.Ident{ + Name: "GetGlobalDBByDBName", + }, + }, + Args: []ast.Expr{ + &ast.BasicLit{ + Kind: token.STRING, + Value: fmt.Sprintf("\"%s\"", a.Business), + }, + }, + }, + }, + } + + // 添加 businessDb.AutoMigrate() 方法 + autoMigrateCall := &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: a.Business + "Db", + }, + Sel: &ast.Ident{ + Name: "AutoMigrate", + }, + }, + }, + } + + returnNode := astBody.List[len(astBody.List)-1] + astBody.List = append(astBody.List[:len(astBody.List)-1], assignNode, autoMigrateCall, returnNode) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_gorm_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_gorm_test.go new file mode 100644 index 000000000..af5cef9e8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_gorm_test.go @@ -0,0 +1,171 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPackageInitializeGorm_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + PackageName string + IsNew bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 &example.ExaFileUploadAndDownload{} 注入", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaFileUploadAndDownload", + PackageName: "example", + IsNew: false, + }, + }, + { + name: "测试 &example.ExaCustomer{} 注入", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaCustomer", + PackageName: "example", + IsNew: false, + }, + }, + { + name: "测试 new(example.ExaFileUploadAndDownload) 注入", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaFileUploadAndDownload", + PackageName: "example", + IsNew: true, + }, + }, + { + name: "测试 new(example.ExaCustomer) 注入", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaCustomer", + PackageName: "example", + IsNew: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageInitializeGorm{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + PackageName: tt.fields.PackageName, + IsNew: tt.fields.IsNew, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPackageInitializeGorm_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + PackageName string + IsNew bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 &example.ExaFileUploadAndDownload{} 回滚", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaFileUploadAndDownload", + PackageName: "example", + IsNew: false, + }, + }, + { + name: "测试 &example.ExaCustomer{} 回滚", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaCustomer", + PackageName: "example", + IsNew: false, + }, + }, + { + name: "测试 new(example.ExaFileUploadAndDownload) 回滚", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaFileUploadAndDownload", + PackageName: "example", + IsNew: true, + }, + }, + { + name: "测试 new(example.ExaCustomer) 回滚", + fields: fields{ + Type: TypePackageInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/model/example"`, + StructName: "ExaCustomer", + PackageName: "example", + IsNew: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageInitializeGorm{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + PackageName: tt.fields.PackageName, + IsNew: tt.fields.IsNew, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_router.go new file mode 100644 index 000000000..9fe4429db --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_router.go @@ -0,0 +1,150 @@ +package ast + +import ( + "fmt" + "go/ast" + "go/token" + "io" +) + +// PackageInitializeRouter 包初始化路由 +// ModuleName := PackageName.AppName.GroupName +// ModuleName.FunctionName(RouterGroupName) +type PackageInitializeRouter struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + RelativePath string // 相对路径 + AppName string // 应用名称 + GroupName string // 分组名称 + ModuleName string // 模块名称 + PackageName string // 包名 + FunctionName string // 函数名 + RouterGroupName string // 路由分组名称 + LeftRouterGroupName string // 左路由分组名称 + RightRouterGroupName string // 右路由分组名称 +} + +func (a *PackageInitializeRouter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PackageInitializeRouter) Rollback(file *ast.File) error { + funcDecl := FindFunction(file, "initBizRouter") + exprNum := 0 + for i := range funcDecl.Body.List { + if IsBlockStmt(funcDecl.Body.List[i]) { + if VariableExistsInBlock(funcDecl.Body.List[i].(*ast.BlockStmt), a.ModuleName) { + for ii, stmt := range funcDecl.Body.List[i].(*ast.BlockStmt).List { + // 检查语句是否为 *ast.ExprStmt + exprStmt, ok := stmt.(*ast.ExprStmt) + if !ok { + continue + } + // 检查表达式是否为 *ast.CallExpr + callExpr, ok := exprStmt.X.(*ast.CallExpr) + if !ok { + continue + } + // 检查是否调用了我们正在寻找的函数 + selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + // 检查调用的函数是否为 systemRouter.InitApiRouter + ident, ok := selExpr.X.(*ast.Ident) + //只要存在调用则+1 + if ok && ident.Name == a.ModuleName { + exprNum++ + } + //判断是否为目标结构 + if !ok || ident.Name != a.ModuleName || selExpr.Sel.Name != a.FunctionName { + continue + } + exprNum-- + // 从语句列表中移除。 + funcDecl.Body.List[i].(*ast.BlockStmt).List = append(funcDecl.Body.List[i].(*ast.BlockStmt).List[:ii], funcDecl.Body.List[i].(*ast.BlockStmt).List[ii+1:]...) + // 如果不再存在任何调用,则删除导入和变量。 + if exprNum == 0 { + funcDecl.Body.List = append(funcDecl.Body.List[:i], funcDecl.Body.List[i+1:]...) + } + break + } + break + } + } + } + + return nil +} + +func (a *PackageInitializeRouter) Injection(file *ast.File) error { + funcDecl := FindFunction(file, "initBizRouter") + hasRouter := false + var varBlock *ast.BlockStmt + for i := range funcDecl.Body.List { + if IsBlockStmt(funcDecl.Body.List[i]) { + if VariableExistsInBlock(funcDecl.Body.List[i].(*ast.BlockStmt), a.ModuleName) { + hasRouter = true + varBlock = funcDecl.Body.List[i].(*ast.BlockStmt) + break + } + } + } + if !hasRouter { + stmt := a.CreateAssignStmt() + varBlock = &ast.BlockStmt{ + List: []ast.Stmt{ + stmt, + }, + } + } + routerStmt := CreateStmt(fmt.Sprintf("%s.%s(%s,%s)", a.ModuleName, a.FunctionName, a.LeftRouterGroupName, a.RightRouterGroupName)) + varBlock.List = append(varBlock.List, routerStmt) + if !hasRouter { + funcDecl.Body.List = append(funcDecl.Body.List, varBlock) + } + return nil +} + +func (a *PackageInitializeRouter) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} + +func (a *PackageInitializeRouter) CreateAssignStmt() *ast.AssignStmt { + //创建左侧变量 + ident := &ast.Ident{ + Name: a.ModuleName, + } + + //创建右侧的赋值语句 + selector := &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.AppName}, + }, + Sel: &ast.Ident{Name: a.GroupName}, + } + + // 创建一个组合的赋值语句 + stmt := &ast.AssignStmt{ + Lhs: []ast.Expr{ident}, + Tok: token.DEFINE, + Rhs: []ast.Expr{selector}, + } + + return stmt +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_router_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_router_test.go new file mode 100644 index 000000000..5a23dbb34 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_initialize_router_test.go @@ -0,0 +1,158 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPackageInitializeRouter_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + AppName string + GroupName string + ModuleName string + PackageName string + FunctionName string + RouterGroupName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 InitCustomerRouter 注入", + fields: fields{ + Type: TypePackageInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`, + AppName: "RouterGroupApp", + GroupName: "Example", + ModuleName: "exampleRouter", + PackageName: "router", + FunctionName: "InitCustomerRouter", + RouterGroupName: "privateGroup", + }, + wantErr: false, + }, + { + name: "测试 InitFileUploadAndDownloadRouter 注入", + fields: fields{ + Type: TypePackageInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`, + AppName: "RouterGroupApp", + GroupName: "Example", + ModuleName: "exampleRouter", + PackageName: "router", + FunctionName: "InitFileUploadAndDownloadRouter", + RouterGroupName: "privateGroup", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageInitializeRouter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + AppName: tt.fields.AppName, + GroupName: tt.fields.GroupName, + ModuleName: tt.fields.ModuleName, + PackageName: tt.fields.PackageName, + FunctionName: tt.fields.FunctionName, + RouterGroupName: tt.fields.RouterGroupName, + LeftRouterGroupName: "privateGroup", + RightRouterGroupName: "publicGroup", + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPackageInitializeRouter_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + AppName string + GroupName string + ModuleName string + PackageName string + FunctionName string + RouterGroupName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + + { + name: "测试 InitCustomerRouter 回滚", + fields: fields{ + Type: TypePackageInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`, + AppName: "RouterGroupApp", + GroupName: "Example", + ModuleName: "exampleRouter", + PackageName: "router", + FunctionName: "InitCustomerRouter", + RouterGroupName: "privateGroup", + }, + wantErr: false, + }, + { + name: "测试 InitFileUploadAndDownloadRouter 回滚", + fields: fields{ + Type: TypePackageInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/router"`, + AppName: "RouterGroupApp", + GroupName: "Example", + ModuleName: "exampleRouter", + PackageName: "router", + FunctionName: "InitFileUploadAndDownloadRouter", + RouterGroupName: "privateGroup", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageInitializeRouter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + AppName: tt.fields.AppName, + GroupName: tt.fields.GroupName, + ModuleName: tt.fields.ModuleName, + PackageName: tt.fields.PackageName, + FunctionName: tt.fields.FunctionName, + RouterGroupName: tt.fields.RouterGroupName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_module_enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_module_enter.go new file mode 100644 index 000000000..881fb3ff7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_module_enter.go @@ -0,0 +1,180 @@ +package ast + +import ( + "go/ast" + "go/token" + "io" +) + +// PackageModuleEnter 模块化入口 +// ModuleName := PackageName.AppName.GroupName.ServiceName +type PackageModuleEnter struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + RelativePath string // 相对路径 + StructName string // 结构体名称 + AppName string // 应用名称 + GroupName string // 分组名称 + ModuleName string // 模块名称 + PackageName string // 包名 + ServiceName string // 服务名称 +} + +func (a *PackageModuleEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PackageModuleEnter) Rollback(file *ast.File) error { + for i := 0; i < len(file.Decls); i++ { + v1, o1 := file.Decls[i].(*ast.GenDecl) + if o1 { + for j := 0; j < len(v1.Specs); j++ { + v2, o2 := v1.Specs[j].(*ast.TypeSpec) + if o2 { + if v2.Name.Name != a.Type.Group() { + continue + } + v3, o3 := v2.Type.(*ast.StructType) + if o3 { + for k := 0; k < len(v3.Fields.List); k++ { + v4, o4 := v3.Fields.List[k].Type.(*ast.Ident) + if o4 && v4.Name == a.StructName { + v3.Fields.List = append(v3.Fields.List[:k], v3.Fields.List[k+1:]...) + } + } + } + continue + } + if a.Type == TypePackageServiceModuleEnter { + continue + } + v3, o3 := v1.Specs[j].(*ast.ValueSpec) + if o3 { + if len(v3.Names) == 1 && v3.Names[0].Name == a.ModuleName { + v1.Specs = append(v1.Specs[:j], v1.Specs[j+1:]...) + } + } + if v1.Tok == token.VAR && len(v1.Specs) == 0 { + _ = NewImport(a.ImportPath).Rollback(file) + if i == len(file.Decls) { + file.Decls = append(file.Decls[:i-1]) + break + } // 空的var(), 如果不删除则会影响的注入变量, 因为识别不到*ast.ValueSpec + file.Decls = append(file.Decls[:i], file.Decls[i+1:]...) + } + } + } + } + return nil +} + +func (a *PackageModuleEnter) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + var hasValue bool + var hasVariables bool + for i := 0; i < len(file.Decls); i++ { + v1, o1 := file.Decls[i].(*ast.GenDecl) + if o1 { + if v1.Tok == token.VAR { + hasVariables = true + } + for j := 0; j < len(v1.Specs); j++ { + if a.Type == TypePackageServiceModuleEnter { + hasValue = true + } + v2, o2 := v1.Specs[j].(*ast.TypeSpec) + if o2 { + if v2.Name.Name != a.Type.Group() { + continue + } + v3, o3 := v2.Type.(*ast.StructType) + if o3 { + var hasStruct bool + for k := 0; k < len(v3.Fields.List); k++ { + v4, o4 := v3.Fields.List[k].Type.(*ast.Ident) + if o4 && v4.Name == a.StructName { + hasStruct = true + } + } + if !hasStruct { + field := &ast.Field{Type: &ast.Ident{Name: a.StructName}} + v3.Fields.List = append(v3.Fields.List, field) + } + } + continue + } + v3, o3 := v1.Specs[j].(*ast.ValueSpec) + if o3 { + hasVariables = true + if len(v3.Names) == 1 && v3.Names[0].Name == a.ModuleName { + hasValue = true + } + } + if v1.Tok == token.VAR && len(v1.Specs) == 0 { + hasVariables = false + } // 说明是空var() + if hasVariables && !hasValue { + spec := &ast.ValueSpec{ + Names: []*ast.Ident{{Name: a.ModuleName}}, + Values: []ast.Expr{ + &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.AppName}, + }, + Sel: &ast.Ident{Name: a.GroupName}, + }, + Sel: &ast.Ident{Name: a.ServiceName}, + }, + }, + } + v1.Specs = append(v1.Specs, spec) + hasValue = true + } + } + } + } + if !hasValue && !hasVariables { + decl := &ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{{Name: a.ModuleName}}, + Values: []ast.Expr{ + &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.AppName}, + }, + Sel: &ast.Ident{Name: a.GroupName}, + }, + Sel: &ast.Ident{Name: a.ServiceName}, + }, + }, + }, + }, + } + file.Decls = append(file.Decls, decl) + } + return nil +} + +func (a *PackageModuleEnter) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_module_enter_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_module_enter_test.go new file mode 100644 index 000000000..0015e3588 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/package_module_enter_test.go @@ -0,0 +1,185 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPackageModuleEnter_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + AppName string + GroupName string + ModuleName string + PackageName string + ServiceName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 FileUploadAndDownloadRouter 回滚", + fields: fields{ + Type: TypePackageRouterModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "example", "enter.go"), + ImportPath: `api "github.com/flipped-aurora/gin-vue-admin/server/api/v1"`, + StructName: "FileUploadAndDownloadRouter", + AppName: "ApiGroupApp", + GroupName: "ExampleApiGroup", + ModuleName: "exaFileUploadAndDownloadApi", + PackageName: "api", + ServiceName: "FileUploadAndDownloadApi", + }, + wantErr: false, + }, + { + name: "测试 FileUploadAndDownloadApi 回滚", + fields: fields{ + Type: TypePackageApiModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "example", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service"`, + StructName: "FileUploadAndDownloadApi", + AppName: "ServiceGroupApp", + GroupName: "ExampleServiceGroup", + ModuleName: "fileUploadAndDownloadService", + PackageName: "service", + ServiceName: "FileUploadAndDownloadService", + }, + wantErr: false, + }, + { + name: "测试 FileUploadAndDownloadService 回滚", + fields: fields{ + Type: TypePackageServiceModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "example", "enter.go"), + ImportPath: ``, + StructName: "FileUploadAndDownloadService", + AppName: "", + GroupName: "", + ModuleName: "", + PackageName: "", + ServiceName: "", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageModuleEnter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + AppName: tt.fields.AppName, + GroupName: tt.fields.GroupName, + ModuleName: tt.fields.ModuleName, + PackageName: tt.fields.PackageName, + ServiceName: tt.fields.ServiceName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPackageModuleEnter_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + AppName string + GroupName string + ModuleName string + PackageName string + ServiceName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 FileUploadAndDownloadRouter 注入", + fields: fields{ + Type: TypePackageRouterModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", "example", "enter.go"), + ImportPath: `api "github.com/flipped-aurora/gin-vue-admin/server/api/v1"`, + StructName: "FileUploadAndDownloadRouter", + AppName: "ApiGroupApp", + GroupName: "ExampleApiGroup", + ModuleName: "exaFileUploadAndDownloadApi", + PackageName: "api", + ServiceName: "FileUploadAndDownloadApi", + }, + wantErr: false, + }, + { + name: "测试 FileUploadAndDownloadApi 注入", + fields: fields{ + Type: TypePackageApiModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", "example", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/service"`, + StructName: "FileUploadAndDownloadApi", + AppName: "ServiceGroupApp", + GroupName: "ExampleServiceGroup", + ModuleName: "fileUploadAndDownloadService", + PackageName: "service", + ServiceName: "FileUploadAndDownloadService", + }, + wantErr: false, + }, + { + name: "测试 FileUploadAndDownloadService 注入", + fields: fields{ + Type: TypePackageServiceModuleEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", "example", "enter.go"), + ImportPath: ``, + StructName: "FileUploadAndDownloadService", + AppName: "", + GroupName: "", + ModuleName: "", + PackageName: "", + ServiceName: "", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PackageModuleEnter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + AppName: tt.fields.AppName, + GroupName: tt.fields.GroupName, + ModuleName: tt.fields.ModuleName, + PackageName: tt.fields.PackageName, + ServiceName: tt.fields.ServiceName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_enter.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_enter.go new file mode 100644 index 000000000..df5bba4d1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_enter.go @@ -0,0 +1,167 @@ +package ast + +import ( + "go/ast" + "go/token" + "io" +) + +// PluginEnter 插件化入口 +// ModuleName := PackageName.GroupName.ServiceName +type PluginEnter struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + RelativePath string // 相对路径 + StructName string // 结构体名称 + StructCamelName string // 结构体小驼峰名称 + ModuleName string // 模块名称 + GroupName string // 分组名称 + PackageName string // 包名 + ServiceName string // 服务名称 +} + +func (a *PluginEnter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PluginEnter) Rollback(file *ast.File) error { + //回滚结构体内内容 + var structType *ast.StructType + ast.Inspect(file, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.TypeSpec: + if s, ok := x.Type.(*ast.StructType); ok { + structType = s + for i, field := range x.Type.(*ast.StructType).Fields.List { + if len(field.Names) > 0 && field.Names[0].Name == a.StructName { + s.Fields.List = append(s.Fields.List[:i], s.Fields.List[i+1:]...) + return false + } + } + } + } + return true + }) + + if len(structType.Fields.List) == 0 { + _ = NewImport(a.ImportPath).Rollback(file) + } + + if a.Type == TypePluginServiceEnter { + return nil + } + + //回滚变量内容 + ast.Inspect(file, func(n ast.Node) bool { + genDecl, ok := n.(*ast.GenDecl) + if ok && genDecl.Tok == token.VAR { + for i, spec := range genDecl.Specs { + valueSpec, vsok := spec.(*ast.ValueSpec) + if vsok { + for _, name := range valueSpec.Names { + if name.Name == a.ModuleName { + genDecl.Specs = append(genDecl.Specs[:i], genDecl.Specs[i+1:]...) + return false + } + } + } + } + } + return true + }) + + return nil +} + +func (a *PluginEnter) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + + has := false + hasVar := false + var firstStruct *ast.StructType + var varSpec *ast.GenDecl + //寻找是否存在结构且定位 + ast.Inspect(file, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.TypeSpec: + if s, ok := x.Type.(*ast.StructType); ok { + firstStruct = s + for _, field := range x.Type.(*ast.StructType).Fields.List { + if len(field.Names) > 0 && field.Names[0].Name == a.StructName { + has = true + return false + } + } + } + } + return true + }) + + if !has { + field := &ast.Field{ + Names: []*ast.Ident{{Name: a.StructName}}, + Type: &ast.Ident{Name: a.StructCamelName}, + } + firstStruct.Fields.List = append(firstStruct.Fields.List, field) + } + + if a.Type == TypePluginServiceEnter { + return nil + } + + //寻找是否存在变量且定位 + ast.Inspect(file, func(n ast.Node) bool { + genDecl, ok := n.(*ast.GenDecl) + if ok && genDecl.Tok == token.VAR { + for _, spec := range genDecl.Specs { + valueSpec, vsok := spec.(*ast.ValueSpec) + if vsok { + varSpec = genDecl + for _, name := range valueSpec.Names { + if name.Name == a.ModuleName { + hasVar = true + return false + } + } + } + } + } + return true + }) + + if !hasVar { + spec := &ast.ValueSpec{ + Names: []*ast.Ident{{Name: a.ModuleName}}, + Values: []ast.Expr{ + &ast.SelectorExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.GroupName}, + }, + Sel: &ast.Ident{Name: a.ServiceName}, + }, + }, + } + varSpec.Specs = append(varSpec.Specs, spec) + } + + return nil +} + +func (a *PluginEnter) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_enter_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_enter_test.go new file mode 100644 index 000000000..60b8dfca6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_enter_test.go @@ -0,0 +1,200 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPluginEnter_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + StructCamelName string + ModuleName string + GroupName string + PackageName string + ServiceName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 Gva插件UserApi 注入", + fields: fields{ + Type: TypePluginApiEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "api", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/service"`, + StructName: "User", + StructCamelName: "user", + ModuleName: "serviceUser", + GroupName: "Service", + PackageName: "service", + ServiceName: "User", + }, + wantErr: false, + }, + { + name: "测试 Gva插件UserRouter 注入", + fields: fields{ + Type: TypePluginRouterEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "router", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/api"`, + StructName: "User", + StructCamelName: "user", + ModuleName: "userApi", + GroupName: "Api", + PackageName: "api", + ServiceName: "User", + }, + wantErr: false, + }, + { + name: "测试 Gva插件UserService 注入", + fields: fields{ + Type: TypePluginServiceEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "service", "enter.go"), + ImportPath: "", + StructName: "User", + StructCamelName: "user", + ModuleName: "", + GroupName: "", + PackageName: "", + ServiceName: "", + }, + wantErr: false, + }, + { + name: "测试 gva的User 注入", + fields: fields{ + Type: TypePluginServiceEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "service", "enter.go"), + ImportPath: "", + StructName: "User", + StructCamelName: "user", + ModuleName: "", + GroupName: "", + PackageName: "", + ServiceName: "", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginEnter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + StructCamelName: tt.fields.StructCamelName, + ModuleName: tt.fields.ModuleName, + GroupName: tt.fields.GroupName, + PackageName: tt.fields.PackageName, + ServiceName: tt.fields.ServiceName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPluginEnter_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + StructCamelName string + ModuleName string + GroupName string + PackageName string + ServiceName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 Gva插件UserRouter 回滚", + fields: fields{ + Type: TypePluginRouterEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "router", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/api"`, + StructName: "User", + StructCamelName: "user", + ModuleName: "userApi", + GroupName: "Api", + PackageName: "api", + ServiceName: "User", + }, + wantErr: false, + }, + { + name: "测试 Gva插件UserApi 回滚", + fields: fields{ + Type: TypePluginApiEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "api", "enter.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/service"`, + StructName: "User", + StructCamelName: "user", + ModuleName: "serviceUser", + GroupName: "Service", + PackageName: "service", + ServiceName: "User", + }, + wantErr: false, + }, + { + name: "测试 Gva插件UserService 回滚", + fields: fields{ + Type: TypePluginServiceEnter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "service", "enter.go"), + ImportPath: "", + StructName: "User", + StructCamelName: "user", + ModuleName: "", + GroupName: "", + PackageName: "", + ServiceName: "", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginEnter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + StructCamelName: tt.fields.StructCamelName, + ModuleName: tt.fields.ModuleName, + GroupName: tt.fields.GroupName, + PackageName: tt.fields.PackageName, + ServiceName: tt.fields.ServiceName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_gen.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_gen.go new file mode 100644 index 000000000..ed7d04fd9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_gen.go @@ -0,0 +1,189 @@ +package ast + +import ( + "go/ast" + "go/token" + "io" +) + +type PluginGen struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + RelativePath string // 相对路径 + StructName string // 结构体名称 + PackageName string // 包名 + IsNew bool // 是否使用new关键字 +} + +func (a *PluginGen) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} +func (a *PluginGen) Rollback(file *ast.File) error { + for i := 0; i < len(file.Decls); i++ { + v1, o1 := file.Decls[i].(*ast.FuncDecl) + if o1 { + for j := 0; j < len(v1.Body.List); j++ { + v2, o2 := v1.Body.List[j].(*ast.ExprStmt) + if o2 { + v3, o3 := v2.X.(*ast.CallExpr) + if o3 { + v4, o4 := v3.Fun.(*ast.SelectorExpr) + if o4 { + if v4.Sel.Name != "ApplyBasic" { + continue + } + for k := 0; k < len(v3.Args); k++ { + v5, o5 := v3.Args[k].(*ast.CallExpr) + if o5 { + v6, o6 := v5.Fun.(*ast.Ident) + if o6 { + if v6.Name != "new" { + continue + } + for l := 0; l < len(v5.Args); l++ { + v7, o7 := v5.Args[l].(*ast.SelectorExpr) + if o7 { + v8, o8 := v7.X.(*ast.Ident) + if o8 { + if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { + v3.Args = append(v3.Args[:k], v3.Args[k+1:]...) + continue + } + } + } + } + } + } + if k >= len(v3.Args) { + break + } + v6, o6 := v3.Args[k].(*ast.CompositeLit) + if o6 { + v7, o7 := v6.Type.(*ast.SelectorExpr) + if o7 { + v8, o8 := v7.X.(*ast.Ident) + if o8 { + if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { + v3.Args = append(v3.Args[:k], v3.Args[k+1:]...) + continue + } + } + } + } + } + if len(v3.Args) == 0 { + _ = NewImport(a.ImportPath).Rollback(file) + } + } + } + } + } + } + } + return nil +} + +func (a *PluginGen) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + for i := 0; i < len(file.Decls); i++ { + v1, o1 := file.Decls[i].(*ast.FuncDecl) + if o1 { + for j := 0; j < len(v1.Body.List); j++ { + v2, o2 := v1.Body.List[j].(*ast.ExprStmt) + if o2 { + v3, o3 := v2.X.(*ast.CallExpr) + if o3 { + v4, o4 := v3.Fun.(*ast.SelectorExpr) + if o4 { + if v4.Sel.Name != "ApplyBasic" { + continue + } + var has bool + for k := 0; k < len(v3.Args); k++ { + v5, o5 := v3.Args[k].(*ast.CallExpr) + if o5 { + v6, o6 := v5.Fun.(*ast.Ident) + if o6 { + if v6.Name != "new" { + continue + } + for l := 0; l < len(v5.Args); l++ { + v7, o7 := v5.Args[l].(*ast.SelectorExpr) + if o7 { + v8, o8 := v7.X.(*ast.Ident) + if o8 { + if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { + has = true + break + } + } + } + } + } + } + v6, o6 := v3.Args[k].(*ast.CompositeLit) + if o6 { + v7, o7 := v6.Type.(*ast.SelectorExpr) + if o7 { + v8, o8 := v7.X.(*ast.Ident) + if o8 { + if v8.Name == a.PackageName && v7.Sel.Name == a.StructName { + has = true + break + } + } + } + } + } + if !has { + if a.IsNew { + arg := &ast.CallExpr{ + Fun: &ast.Ident{Name: "\n\t\tnew"}, + Args: []ast.Expr{ + &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.StructName}, + }, + }, + } + v3.Args = append(v3.Args, arg) + v3.Args = append(v3.Args, &ast.BasicLit{ + Kind: token.STRING, + Value: "\n", + }) + break + } + arg := &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.StructName}, + }, + } + v3.Args = append(v3.Args, arg) + } + } + } + } + } + } + } + return nil +} + +func (a *PluginGen) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_gen_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_gen_test.go new file mode 100644 index 000000000..1b9c790ef --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_gen_test.go @@ -0,0 +1,127 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPluginGenModel_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + PackageName string + StructName string + IsNew bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 GvaUser 结构体注入", + fields: fields{ + Type: TypePluginGen, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + PackageName: "model", + StructName: "User", + IsNew: false, + }, + }, + { + name: "测试 GvaUser 结构体注入", + fields: fields{ + Type: TypePluginGen, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + PackageName: "model", + StructName: "User", + IsNew: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginGen{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + PackageName: tt.fields.PackageName, + StructName: tt.fields.StructName, + IsNew: tt.fields.IsNew, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPluginGenModel_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + PackageName string + StructName string + IsNew bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 GvaUser 回滚", + fields: fields{ + Type: TypePluginGen, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + PackageName: "model", + StructName: "User", + IsNew: false, + }, + }, + { + name: "测试 GvaUser 回滚", + fields: fields{ + Type: TypePluginGen, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "gen", "main.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + PackageName: "model", + StructName: "User", + IsNew: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginGen{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + PackageName: tt.fields.PackageName, + StructName: tt.fields.StructName, + IsNew: tt.fields.IsNew, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_gorm.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_gorm.go new file mode 100644 index 000000000..e3422518c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_gorm.go @@ -0,0 +1,111 @@ +package ast + +import ( + "go/ast" + "io" +) + +type PluginInitializeGorm struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + RelativePath string // 相对路径 + StructName string // 结构体名称 + PackageName string // 包名 + IsNew bool // 是否使用new关键字 true: new(PackageName.StructName) false: &PackageName.StructName{} +} + +func (a *PluginInitializeGorm) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PluginInitializeGorm) Rollback(file *ast.File) error { + var needRollBackImport bool + ast.Inspect(file, func(n ast.Node) bool { + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + selExpr, seok := callExpr.Fun.(*ast.SelectorExpr) + if !seok || selExpr.Sel.Name != "AutoMigrate" { + return true + } + if len(callExpr.Args) <= 1 { + needRollBackImport = true + } + // 删除指定的参数 + for i, arg := range callExpr.Args { + compLit, cok := arg.(*ast.CompositeLit) + if !cok { + continue + } + + cselExpr, sok := compLit.Type.(*ast.SelectorExpr) + if !sok { + continue + } + + ident, idok := cselExpr.X.(*ast.Ident) + if idok && ident.Name == a.PackageName && cselExpr.Sel.Name == a.StructName { + // 删除参数 + callExpr.Args = append(callExpr.Args[:i], callExpr.Args[i+1:]...) + break + } + } + + return true + }) + + if needRollBackImport { + _ = NewImport(a.ImportPath).Rollback(file) + } + + return nil +} + +func (a *PluginInitializeGorm) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + var call *ast.CallExpr + ast.Inspect(file, func(n ast.Node) bool { + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) + if ok && selExpr.Sel.Name == "AutoMigrate" { + call = callExpr + return false + } + + return true + }) + + arg := &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: &ast.Ident{Name: a.PackageName}, + Sel: &ast.Ident{Name: a.StructName}, + }, + } + + call.Args = append(call.Args, arg) + return nil +} + +func (a *PluginInitializeGorm) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_gorm_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_gorm_test.go new file mode 100644 index 000000000..ebfc12a12 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_gorm_test.go @@ -0,0 +1,138 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPluginInitializeGorm_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + PackageName string + IsNew bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 &model.User{} 注入", + fields: fields{ + Type: TypePluginInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + StructName: "User", + PackageName: "model", + IsNew: false, + }, + }, + { + name: "测试 new(model.ExaCustomer) 注入", + fields: fields{ + Type: TypePluginInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + StructName: "User", + PackageName: "model", + IsNew: true, + }, + }, + { + name: "测试 new(model.SysUsers) 注入", + fields: fields{ + Type: TypePluginInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + StructName: "SysUser", + PackageName: "model", + IsNew: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginInitializeGorm{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + PackageName: tt.fields.PackageName, + IsNew: tt.fields.IsNew, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPluginInitializeGorm_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + StructName string + PackageName string + IsNew bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 &model.User{} 回滚", + fields: fields{ + Type: TypePluginInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + StructName: "User", + PackageName: "model", + IsNew: false, + }, + }, + { + name: "测试 new(model.ExaCustomer) 回滚", + fields: fields{ + Type: TypePluginInitializeGorm, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "gorm.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/model"`, + StructName: "User", + PackageName: "model", + IsNew: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginInitializeGorm{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + StructName: tt.fields.StructName, + PackageName: tt.fields.PackageName, + IsNew: tt.fields.IsNew, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_router.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_router.go new file mode 100644 index 000000000..6550789ed --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_router.go @@ -0,0 +1,124 @@ +package ast + +import ( + "fmt" + "go/ast" + "io" +) + +// PluginInitializeRouter 插件初始化路由 +// PackageName.AppName.GroupName.FunctionName() +type PluginInitializeRouter struct { + Base + Type Type // 类型 + Path string // 文件路径 + ImportPath string // 导包路径 + ImportGlobalPath string // 导包全局变量路径 + ImportMiddlewarePath string // 导包中间件路径 + RelativePath string // 相对路径 + AppName string // 应用名称 + GroupName string // 分组名称 + PackageName string // 包名 + FunctionName string // 函数名 + LeftRouterGroupName string // 左路由分组名称 + RightRouterGroupName string // 右路由分组名称 +} + +func (a *PluginInitializeRouter) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.Path + a.RelativePath = a.Base.RelativePath(a.Path) + return a.Base.Parse(filename, writer) + } + a.Path = a.Base.AbsolutePath(a.RelativePath) + filename = a.Path + } + return a.Base.Parse(filename, writer) +} + +func (a *PluginInitializeRouter) Rollback(file *ast.File) error { + funcDecl := FindFunction(file, "Router") + delI := 0 + routerNum := 0 + for i := len(funcDecl.Body.List) - 1; i >= 0; i-- { + stmt, ok := funcDecl.Body.List[i].(*ast.ExprStmt) + if !ok { + continue + } + + callExpr, ok := stmt.X.(*ast.CallExpr) + if !ok { + continue + } + + selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + + ident, ok := selExpr.X.(*ast.SelectorExpr) + + if ok { + if iExpr, ieok := ident.X.(*ast.SelectorExpr); ieok { + if iden, idok := iExpr.X.(*ast.Ident); idok { + if iden.Name == "router" { + routerNum++ + } + } + } + if ident.Sel.Name == a.GroupName && selExpr.Sel.Name == a.FunctionName { + // 删除语句 + delI = i + } + } + } + + funcDecl.Body.List = append(funcDecl.Body.List[:delI], funcDecl.Body.List[delI+1:]...) + + if routerNum <= 1 { + _ = NewImport(a.ImportPath).Rollback(file) + } + + return nil +} + +func (a *PluginInitializeRouter) Injection(file *ast.File) error { + _ = NewImport(a.ImportPath).Injection(file) + funcDecl := FindFunction(file, "Router") + + var exists bool + + ast.Inspect(funcDecl, func(n ast.Node) bool { + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + + ident, ok := selExpr.X.(*ast.SelectorExpr) + if ok && ident.Sel.Name == a.GroupName && selExpr.Sel.Name == a.FunctionName { + exists = true + return false + } + return true + }) + + if !exists { + stmtStr := fmt.Sprintf("%s.%s.%s.%s(%s, %s)", a.PackageName, a.AppName, a.GroupName, a.FunctionName, a.LeftRouterGroupName, a.RightRouterGroupName) + stmt := CreateStmt(stmtStr) + funcDecl.Body.List = append(funcDecl.Body.List, stmt) + } + return nil +} + +func (a *PluginInitializeRouter) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.Path + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_router_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_router_test.go new file mode 100644 index 000000000..4dffd7fc4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_router_test.go @@ -0,0 +1,155 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPluginInitializeRouter_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + AppName string + GroupName string + PackageName string + FunctionName string + LeftRouterGroupName string + RightRouterGroupName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 Gva插件User 注入", + fields: fields{ + Type: TypePluginInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`, + AppName: "Router", + GroupName: "User", + PackageName: "router", + FunctionName: "Init", + LeftRouterGroupName: "public", + RightRouterGroupName: "private", + }, + wantErr: false, + }, + { + name: "测试 中文 注入", + fields: fields{ + Type: TypePluginInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`, + AppName: "Router", + GroupName: "U中文", + PackageName: "router", + FunctionName: "Init", + LeftRouterGroupName: "public", + RightRouterGroupName: "private", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginInitializeRouter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + AppName: tt.fields.AppName, + GroupName: tt.fields.GroupName, + PackageName: tt.fields.PackageName, + FunctionName: tt.fields.FunctionName, + LeftRouterGroupName: tt.fields.LeftRouterGroupName, + RightRouterGroupName: tt.fields.RightRouterGroupName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPluginInitializeRouter_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + ImportPath string + AppName string + GroupName string + PackageName string + FunctionName string + LeftRouterGroupName string + RightRouterGroupName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 Gva插件User 回滚", + fields: fields{ + Type: TypePluginInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`, + AppName: "Router", + GroupName: "User", + PackageName: "router", + FunctionName: "Init", + LeftRouterGroupName: "public", + RightRouterGroupName: "private", + }, + wantErr: false, + }, + { + name: "测试 中文 注入", + fields: fields{ + Type: TypePluginInitializeRouter, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "initialize", "router.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva/router"`, + AppName: "Router", + GroupName: "U中文", + PackageName: "router", + FunctionName: "Init", + LeftRouterGroupName: "public", + RightRouterGroupName: "private", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PluginInitializeRouter{ + Type: tt.fields.Type, + Path: tt.fields.Path, + ImportPath: tt.fields.ImportPath, + AppName: tt.fields.AppName, + GroupName: tt.fields.GroupName, + PackageName: tt.fields.PackageName, + FunctionName: tt.fields.FunctionName, + LeftRouterGroupName: tt.fields.LeftRouterGroupName, + RightRouterGroupName: tt.fields.RightRouterGroupName, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_v2.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_v2.go new file mode 100644 index 000000000..1befdc657 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_v2.go @@ -0,0 +1,52 @@ +package ast + +import ( + "fmt" + "go/ast" + "io" +) + +type PluginInitializeV2 struct { + Base + Type Type // 类型 + Path string // 文件路径 + PluginPath string // 插件路径 + RelativePath string // 相对路径 + ImportPath string // 导包路径 + StructName string // 结构体名称 + PackageName string // 包名 +} + +func (a *PluginInitializeV2) Parse(filename string, writer io.Writer) (file *ast.File, err error) { + if filename == "" { + if a.RelativePath == "" { + filename = a.PluginPath + a.RelativePath = a.Base.RelativePath(a.PluginPath) + return a.Base.Parse(filename, writer) + } + a.PluginPath = a.Base.AbsolutePath(a.RelativePath) + filename = a.PluginPath + } + return a.Base.Parse(filename, writer) +} + +func (a *PluginInitializeV2) Injection(file *ast.File) error { + if !CheckImport(file, a.ImportPath) { + NewImport(a.ImportPath).Injection(file) + funcDecl := FindFunction(file, "bizPluginV2") + stmt := CreateStmt(fmt.Sprintf("PluginInitV2(engine, %s.Plugin)", a.PackageName)) + funcDecl.Body.List = append(funcDecl.Body.List, stmt) + } + return nil +} + +func (a *PluginInitializeV2) Rollback(file *ast.File) error { + return nil +} + +func (a *PluginInitializeV2) Format(filename string, writer io.Writer, file *ast.File) error { + if filename == "" { + filename = a.PluginPath + } + return a.Base.Format(filename, writer, file) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_v2_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_v2_test.go new file mode 100644 index 000000000..4e99c6dae --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/ast/plugin_initialize_v2_test.go @@ -0,0 +1,100 @@ +package ast + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "path/filepath" + "testing" +) + +func TestPluginInitialize_Injection(t *testing.T) { + type fields struct { + Type Type + Path string + PluginPath string + ImportPath string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 Gva插件 注册注入", + fields: fields{ + Type: TypePluginInitializeV2, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"), + PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "plugin.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva"`, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := PluginInitializeV2{ + Type: tt.fields.Type, + Path: tt.fields.Path, + PluginPath: tt.fields.PluginPath, + ImportPath: tt.fields.ImportPath, + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Injection(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Injection() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPluginInitialize_Rollback(t *testing.T) { + type fields struct { + Type Type + Path string + PluginPath string + ImportPath string + PluginName string + StructName string + PackageName string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "测试 Gva插件 回滚", + fields: fields{ + Type: TypePluginInitializeV2, + Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"), + PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", "gva", "plugin.go"), + ImportPath: `"github.com/flipped-aurora/gin-vue-admin/server/plugin/gva"`, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := PluginInitializeV2{ + Type: tt.fields.Type, + Path: tt.fields.Path, + PluginPath: tt.fields.PluginPath, + ImportPath: tt.fields.ImportPath, + StructName: "Plugin", + PackageName: "gva", + } + file, err := a.Parse(a.Path, nil) + if err != nil { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + a.Rollback(file) + err = a.Format(a.Path, nil, file) + if (err != nil) != tt.wantErr { + t.Errorf("Rollback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/breakpoint_continue.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/breakpoint_continue.go new file mode 100644 index 000000000..c0baee57c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/breakpoint_continue.go @@ -0,0 +1,112 @@ +package utils + +import ( + "errors" + "os" + "strconv" + "strings" +) + +// 前端传来文件片与当前片为什么文件的第几片 +// 后端拿到以后比较次分片是否上传 或者是否为不完全片 +// 前端发送每片多大 +// 前端告知是否为最后一片且是否完成 + +const ( + breakpointDir = "./breakpointDir/" + finishDir = "./fileDir/" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: BreakPointContinue +//@description: 断点续传 +//@param: content []byte, fileName string, contentNumber int, contentTotal int, fileMd5 string +//@return: error, string + +func BreakPointContinue(content []byte, fileName string, contentNumber int, contentTotal int, fileMd5 string) (string, error) { + path := breakpointDir + fileMd5 + "/" + err := os.MkdirAll(path, os.ModePerm) + if err != nil { + return path, err + } + pathC, err := makeFileContent(content, fileName, path, contentNumber) + return pathC, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CheckMd5 +//@description: 检查Md5 +//@param: content []byte, chunkMd5 string +//@return: CanUpload bool + +func CheckMd5(content []byte, chunkMd5 string) (CanUpload bool) { + fileMd5 := MD5V(content) + if fileMd5 == chunkMd5 { + return true // 可以继续上传 + } else { + return false // 切片不完整,废弃 + } +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: makeFileContent +//@description: 创建切片内容 +//@param: content []byte, fileName string, FileDir string, contentNumber int +//@return: string, error + +func makeFileContent(content []byte, fileName string, FileDir string, contentNumber int) (string, error) { + if strings.Index(fileName, "..") > -1 || strings.Index(FileDir, "..") > -1 { + return "", errors.New("文件名或路径不合法") + } + path := FileDir + fileName + "_" + strconv.Itoa(contentNumber) + f, err := os.Create(path) + if err != nil { + return path, err + } else { + _, err = f.Write(content) + if err != nil { + return path, err + } + } + defer f.Close() + return path, nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: makeFileContent +//@description: 创建切片文件 +//@param: fileName string, FileMd5 string +//@return: error, string + +func MakeFile(fileName string, FileMd5 string) (string, error) { + rd, err := os.ReadDir(breakpointDir + FileMd5) + if err != nil { + return finishDir + fileName, err + } + _ = os.MkdirAll(finishDir, os.ModePerm) + fd, err := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o644) + if err != nil { + return finishDir + fileName, err + } + defer fd.Close() + for k := range rd { + content, _ := os.ReadFile(breakpointDir + FileMd5 + "/" + fileName + "_" + strconv.Itoa(k)) + _, err = fd.Write(content) + if err != nil { + _ = os.Remove(finishDir + fileName) + return finishDir + fileName, err + } + } + return finishDir + fileName, nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: RemoveChunk +//@description: 移除切片 +//@param: FileMd5 string +//@return: error + +func RemoveChunk(FileMd5 string) error { + err := os.RemoveAll(breakpointDir + FileMd5) + return err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/captcha/redis.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/captcha/redis.go new file mode 100644 index 000000000..a13b7cc11 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/captcha/redis.go @@ -0,0 +1,60 @@ +package captcha + +import ( + "context" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/mojocn/base64Captcha" + "go.uber.org/zap" +) + +func NewDefaultRedisStore() *RedisStore { + return &RedisStore{ + Expiration: time.Second * 180, + PreKey: "CAPTCHA_", + Context: context.TODO(), + } +} + +type RedisStore struct { + Expiration time.Duration + PreKey string + Context context.Context +} + +func (rs *RedisStore) UseWithCtx(ctx context.Context) base64Captcha.Store { + rs.Context = ctx + return rs +} + +func (rs *RedisStore) Set(id string, value string) error { + err := global.GVA_REDIS.Set(rs.Context, rs.PreKey+id, value, rs.Expiration).Err() + if err != nil { + global.GVA_LOG.Error("RedisStoreSetError!", zap.Error(err)) + return err + } + return nil +} + +func (rs *RedisStore) Get(key string, clear bool) string { + val, err := global.GVA_REDIS.Get(rs.Context, key).Result() + if err != nil { + global.GVA_LOG.Error("RedisStoreGetError!", zap.Error(err)) + return "" + } + if clear { + err := global.GVA_REDIS.Del(rs.Context, key).Err() + if err != nil { + global.GVA_LOG.Error("RedisStoreClearError!", zap.Error(err)) + return "" + } + } + return val +} + +func (rs *RedisStore) Verify(id, answer string, clear bool) bool { + key := rs.PreKey + id + v := rs.Get(key, clear) + return v == answer +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/claims.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/claims.go new file mode 100644 index 000000000..69216700a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/claims.go @@ -0,0 +1,150 @@ +package utils + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" + "github.com/gin-gonic/gin" + "github.com/gofrs/uuid/v5" + "net" + "time" +) + +func ClearToken(c *gin.Context) { + // 增加cookie x-token 向来源的web添加 + host, _, err := net.SplitHostPort(c.Request.Host) + if err != nil { + host = c.Request.Host + } + + if net.ParseIP(host) != nil { + c.SetCookie("x-token", "", -1, "/", "", false, false) + } else { + c.SetCookie("x-token", "", -1, "/", host, false, false) + } +} + +func SetToken(c *gin.Context, token string, maxAge int) { + // 增加cookie x-token 向来源的web添加 + host, _, err := net.SplitHostPort(c.Request.Host) + if err != nil { + host = c.Request.Host + } + + if net.ParseIP(host) != nil { + c.SetCookie("x-token", token, maxAge, "/", "", false, false) + } else { + c.SetCookie("x-token", token, maxAge, "/", host, false, false) + } +} + +func GetToken(c *gin.Context) string { + token, _ := c.Cookie("x-token") + if token == "" { + j := NewJWT() + token = c.Request.Header.Get("x-token") + claims, err := j.ParseToken(token) + if err != nil { + global.GVA_LOG.Error("重新写入cookie token失败,未能成功解析token,请检查请求头是否存在x-token且claims是否为规定结构") + return token + } + SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60)) + } + return token +} + +func GetClaims(c *gin.Context) (*systemReq.CustomClaims, error) { + token := GetToken(c) + j := NewJWT() + claims, err := j.ParseToken(token) + if err != nil { + global.GVA_LOG.Error("从Gin的Context中获取从jwt解析信息失败, 请检查请求头是否存在x-token且claims是否为规定结构") + } + return claims, err +} + +// GetUserID 从Gin的Context中获取从jwt解析出来的用户ID +func GetUserID(c *gin.Context) uint { + if claims, exists := c.Get("claims"); !exists { + if cl, err := GetClaims(c); err != nil { + return 0 + } else { + return cl.BaseClaims.ID + } + } else { + waitUse := claims.(*systemReq.CustomClaims) + return waitUse.BaseClaims.ID + } +} + +// GetUserUuid 从Gin的Context中获取从jwt解析出来的用户UUID +func GetUserUuid(c *gin.Context) uuid.UUID { + if claims, exists := c.Get("claims"); !exists { + if cl, err := GetClaims(c); err != nil { + return uuid.UUID{} + } else { + return cl.UUID + } + } else { + waitUse := claims.(*systemReq.CustomClaims) + return waitUse.UUID + } +} + +// GetUserAuthorityId 从Gin的Context中获取从jwt解析出来的用户角色id +func GetUserAuthorityId(c *gin.Context) uint { + if claims, exists := c.Get("claims"); !exists { + if cl, err := GetClaims(c); err != nil { + return 0 + } else { + return cl.AuthorityId + } + } else { + waitUse := claims.(*systemReq.CustomClaims) + return waitUse.AuthorityId + } +} + +// GetUserInfo 从Gin的Context中获取从jwt解析出来的用户角色id +func GetUserInfo(c *gin.Context) *systemReq.CustomClaims { + if claims, exists := c.Get("claims"); !exists { + if cl, err := GetClaims(c); err != nil { + return nil + } else { + return cl + } + } else { + waitUse := claims.(*systemReq.CustomClaims) + return waitUse + } +} + +// GetUserName 从Gin的Context中获取从jwt解析出来的用户名 +func GetUserName(c *gin.Context) string { + if claims, exists := c.Get("claims"); !exists { + if cl, err := GetClaims(c); err != nil { + return "" + } else { + return cl.Username + } + } else { + waitUse := claims.(*systemReq.CustomClaims) + return waitUse.Username + } +} + +func LoginToken(user system.Login) (token string, claims systemReq.CustomClaims, err error) { + j := &JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名 + claims = j.CreateClaims(systemReq.BaseClaims{ + UUID: user.GetUUID(), + ID: user.GetUserId(), + NickName: user.GetNickname(), + Username: user.GetUsername(), + AuthorityId: user.GetAuthorityId(), + }) + token, err = j.CreateToken(claims) + if err != nil { + return + } + return +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/directory.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/directory.go new file mode 100644 index 000000000..d419feefd --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/directory.go @@ -0,0 +1,124 @@ +package utils + +import ( + "errors" + "os" + "path/filepath" + "reflect" + "strings" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "go.uber.org/zap" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: PathExists +//@description: 文件目录是否存在 +//@param: path string +//@return: bool, error + +func PathExists(path string) (bool, error) { + fi, err := os.Stat(path) + if err == nil { + if fi.IsDir() { + return true, nil + } + return false, errors.New("存在同名文件") + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: CreateDir +//@description: 批量创建文件夹 +//@param: dirs ...string +//@return: err error + +func CreateDir(dirs ...string) (err error) { + for _, v := range dirs { + exist, err := PathExists(v) + if err != nil { + return err + } + if !exist { + global.GVA_LOG.Debug("create directory" + v) + if err := os.MkdirAll(v, os.ModePerm); err != nil { + global.GVA_LOG.Error("create directory"+v, zap.Any(" error:", err)) + return err + } + } + } + return err +} + +//@author: [songzhibin97](https://github.com/songzhibin97) +//@function: FileMove +//@description: 文件移动供外部调用 +//@param: src string, dst string(src: 源位置,绝对路径or相对路径, dst: 目标位置,绝对路径or相对路径,必须为文件夹) +//@return: err error + +func FileMove(src string, dst string) (err error) { + if dst == "" { + return nil + } + src, err = filepath.Abs(src) + if err != nil { + return err + } + dst, err = filepath.Abs(dst) + if err != nil { + return err + } + revoke := false + dir := filepath.Dir(dst) +Redirect: + _, err = os.Stat(dir) + if err != nil { + err = os.MkdirAll(dir, 0o755) + if err != nil { + return err + } + if !revoke { + revoke = true + goto Redirect + } + } + return os.Rename(src, dst) +} + +func DeLFile(filePath string) error { + return os.RemoveAll(filePath) +} + +//@author: [songzhibin97](https://github.com/songzhibin97) +//@function: TrimSpace +//@description: 去除结构体空格 +//@param: target interface (target: 目标结构体,传入必须是指针类型) +//@return: null + +func TrimSpace(target interface{}) { + t := reflect.TypeOf(target) + if t.Kind() != reflect.Ptr { + return + } + t = t.Elem() + v := reflect.ValueOf(target).Elem() + for i := 0; i < t.NumField(); i++ { + switch v.Field(i).Kind() { + case reflect.String: + v.Field(i).SetString(strings.TrimSpace(v.Field(i).String())) + } + } +} + +// FileExist 判断文件是否存在 +func FileExist(path string) bool { + fi, err := os.Lstat(path) + if err == nil { + return !fi.IsDir() + } + return !os.IsNotExist(err) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/fmt_plus.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/fmt_plus.go new file mode 100644 index 000000000..8b77d4f26 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/fmt_plus.go @@ -0,0 +1,82 @@ +package utils + +import ( + "fmt" + "math/rand" + "reflect" + "strings" +) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: StructToMap +//@description: 利用反射将结构体转化为map +//@param: obj interface{} +//@return: map[string]interface{} + +func StructToMap(obj interface{}) map[string]interface{} { + obj1 := reflect.TypeOf(obj) + obj2 := reflect.ValueOf(obj) + + data := make(map[string]interface{}) + for i := 0; i < obj1.NumField(); i++ { + if obj1.Field(i).Tag.Get("mapstructure") != "" { + data[obj1.Field(i).Tag.Get("mapstructure")] = obj2.Field(i).Interface() + } else { + data[obj1.Field(i).Name] = obj2.Field(i).Interface() + } + } + return data +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: ArrayToString +//@description: 将数组格式化为字符串 +//@param: array []interface{} +//@return: string + +func ArrayToString(array []interface{}) string { + return strings.Replace(strings.Trim(fmt.Sprint(array), "[]"), " ", ",", -1) +} + +func Pointer[T any](in T) (out *T) { + return &in +} + +func FirstUpper(s string) string { + if s == "" { + return "" + } + return strings.ToUpper(s[:1]) + s[1:] +} + +func FirstLower(s string) string { + if s == "" { + return "" + } + return strings.ToLower(s[:1]) + s[1:] +} + +// MaheHump 将字符串转换为驼峰命名 +func MaheHump(s string) string { + words := strings.Split(s, "-") + + for i := 1; i < len(words); i++ { + words[i] = strings.Title(words[i]) + } + + return strings.Join(words, "") +} + +// 随机字符串 +func RandomString(n int) string { + var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + b := make([]rune, n) + for i := range b { + b[i] = letters[RandomInt(0, len(letters))] + } + return string(b) +} + +func RandomInt(min, max int) int { + return min + rand.Intn(max-min) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/hash.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/hash.go new file mode 100644 index 000000000..9c3564b49 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/hash.go @@ -0,0 +1,31 @@ +package utils + +import ( + "crypto/md5" + "encoding/hex" + "golang.org/x/crypto/bcrypt" +) + +// BcryptHash 使用 bcrypt 对密码进行加密 +func BcryptHash(password string) string { + bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(bytes) +} + +// BcryptCheck 对比明文密码和数据库的哈希值 +func BcryptCheck(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: MD5V +//@description: md5加密 +//@param: str []byte +//@return: string + +func MD5V(str []byte, b ...byte) string { + h := md5.New() + h.Write(str) + return hex.EncodeToString(h.Sum(b)) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/human_duration.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/human_duration.go new file mode 100644 index 000000000..0cdb055c6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/human_duration.go @@ -0,0 +1,29 @@ +package utils + +import ( + "strconv" + "strings" + "time" +) + +func ParseDuration(d string) (time.Duration, error) { + d = strings.TrimSpace(d) + dr, err := time.ParseDuration(d) + if err == nil { + return dr, nil + } + if strings.Contains(d, "d") { + index := strings.Index(d, "d") + + hour, _ := strconv.Atoi(d[:index]) + dr = time.Hour * 24 * time.Duration(hour) + ndr, err := time.ParseDuration(d[index+1:]) + if err != nil { + return dr, nil + } + return dr + ndr, nil + } + + dv, err := strconv.ParseInt(d, 10, 64) + return time.Duration(dv), err +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/human_duration_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/human_duration_test.go new file mode 100644 index 000000000..8a5294b29 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/human_duration_test.go @@ -0,0 +1,49 @@ +package utils + +import ( + "testing" + "time" +) + +func TestParseDuration(t *testing.T) { + type args struct { + d string + } + tests := []struct { + name string + args args + want time.Duration + wantErr bool + }{ + { + name: "5h20m", + args: args{"5h20m"}, + want: time.Hour*5 + 20*time.Minute, + wantErr: false, + }, + { + name: "1d5h20m", + args: args{"1d5h20m"}, + want: 24*time.Hour + time.Hour*5 + 20*time.Minute, + wantErr: false, + }, + { + name: "1d", + args: args{"1d"}, + want: 24 * time.Hour, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseDuration(tt.args.d) + if (err != nil) != tt.wantErr { + t.Errorf("ParseDuration() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ParseDuration() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/json.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/json.go new file mode 100644 index 000000000..8c4118c7b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/json.go @@ -0,0 +1,34 @@ +package utils + +import ( + "encoding/json" + "strings" +) + +func GetJSONKeys(jsonStr string) (keys []string, err error) { + // 使用json.Decoder,以便在解析过程中记录键的顺序 + dec := json.NewDecoder(strings.NewReader(jsonStr)) + t, err := dec.Token() + if err != nil { + return nil, err + } + // 确保数据是一个对象 + if t != json.Delim('{') { + return nil, err + } + for dec.More() { + t, err = dec.Token() + if err != nil { + return nil, err + } + keys = append(keys, t.(string)) + + // 解析值 + var value interface{} + err = dec.Decode(&value) + if err != nil { + return nil, err + } + } + return keys, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/json_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/json_test.go new file mode 100644 index 000000000..f21a67922 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/json_test.go @@ -0,0 +1,53 @@ +package utils + +import ( + "fmt" + "testing" +) + +func TestGetJSONKeys(t *testing.T) { + var jsonStr = ` + { + "Name": "test", + "TableName": "test", + "TemplateID": "test", + "TemplateInfo": "test", + "Limit": 0 +}` + keys, err := GetJSONKeys(jsonStr) + if err != nil { + t.Errorf("GetJSONKeys failed" + err.Error()) + return + } + if len(keys) != 5 { + t.Errorf("GetJSONKeys failed" + err.Error()) + return + } + if keys[0] != "Name" { + t.Errorf("GetJSONKeys failed" + err.Error()) + + return + } + if keys[1] != "TableName" { + t.Errorf("GetJSONKeys failed" + err.Error()) + + return + } + if keys[2] != "TemplateID" { + t.Errorf("GetJSONKeys failed" + err.Error()) + + return + } + if keys[3] != "TemplateInfo" { + t.Errorf("GetJSONKeys failed" + err.Error()) + + return + } + if keys[4] != "Limit" { + t.Errorf("GetJSONKeys failed" + err.Error()) + + return + } + + fmt.Println(keys) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/jwt.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/jwt.go new file mode 100644 index 000000000..c1298a870 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/jwt.go @@ -0,0 +1,88 @@ +package utils + +import ( + "errors" + "time" + + jwt "github.com/golang-jwt/jwt/v4" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" +) + +type JWT struct { + SigningKey []byte +} + +var ( + TokenExpired = errors.New("Token is expired") + TokenNotValidYet = errors.New("Token not active yet") + TokenMalformed = errors.New("That's not even a token") + TokenInvalid = errors.New("Couldn't handle this token:") +) + +func NewJWT() *JWT { + return &JWT{ + []byte(global.GVA_CONFIG.JWT.SigningKey), + } +} + +func (j *JWT) CreateClaims(baseClaims request.BaseClaims) request.CustomClaims { + bf, _ := ParseDuration(global.GVA_CONFIG.JWT.BufferTime) + ep, _ := ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) + claims := request.CustomClaims{ + BaseClaims: baseClaims, + BufferTime: int64(bf / time.Second), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失 + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"GVA"}, // 受众 + NotBefore: jwt.NewNumericDate(time.Now().Add(-1000)), // 签名生效时间 + ExpiresAt: jwt.NewNumericDate(time.Now().Add(ep)), // 过期时间 7天 配置文件 + Issuer: global.GVA_CONFIG.JWT.Issuer, // 签名的发行者 + }, + } + return claims +} + +// 创建一个token +func (j *JWT) CreateToken(claims request.CustomClaims) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(j.SigningKey) +} + +// CreateTokenByOldToken 旧token 换新token 使用归并回源避免并发问题 +func (j *JWT) CreateTokenByOldToken(oldToken string, claims request.CustomClaims) (string, error) { + v, err, _ := global.GVA_Concurrency_Control.Do("JWT:"+oldToken, func() (interface{}, error) { + return j.CreateToken(claims) + }) + return v.(string), err +} + +// 解析 token +func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) { + token, err := jwt.ParseWithClaims(tokenString, &request.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { + return j.SigningKey, nil + }) + if err != nil { + if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + return nil, TokenMalformed + } else if ve.Errors&jwt.ValidationErrorExpired != 0 { + // Token is expired + return nil, TokenExpired + } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { + return nil, TokenNotValidYet + } else { + return nil, TokenInvalid + } + } + } + if token != nil { + if claims, ok := token.Claims.(*request.CustomClaims); ok && token.Valid { + return claims, nil + } + return nil, TokenInvalid + + } else { + return nil, TokenInvalid + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/plugin/plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/plugin/plugin.go new file mode 100644 index 000000000..a59d5b52a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/plugin/plugin.go @@ -0,0 +1,18 @@ +package plugin + +import ( + "github.com/gin-gonic/gin" +) + +const ( + OnlyFuncName = "Plugin" +) + +// Plugin 插件模式接口化 +type Plugin interface { + // Register 注册路由 + Register(group *gin.RouterGroup) + + // RouterPath 用户返回注册路由 + RouterPath() string +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/plugin/v2/plugin.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/plugin/v2/plugin.go new file mode 100644 index 000000000..4dac0ab1a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/plugin/v2/plugin.go @@ -0,0 +1,11 @@ +package plugin + +import ( + "github.com/gin-gonic/gin" +) + +// Plugin 插件模式接口化v2 +type Plugin interface { + // Register 注册路由 + Register(group *gin.Engine) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/reload.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/reload.go new file mode 100644 index 000000000..de5499bf3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/reload.go @@ -0,0 +1,18 @@ +package utils + +import ( + "errors" + "os" + "os/exec" + "runtime" + "strconv" +) + +func Reload() error { + if runtime.GOOS == "windows" { + return errors.New("系统不支持") + } + pid := os.Getpid() + cmd := exec.Command("kill", "-1", strconv.Itoa(pid)) + return cmd.Run() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/request/http.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/request/http.go new file mode 100644 index 000000000..86d0d1509 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/request/http.go @@ -0,0 +1,62 @@ +package request + +import ( + "bytes" + "encoding/json" + "net/http" + "net/url" +) + +func HttpRequest( + urlStr string, + method string, + headers map[string]string, + params map[string]string, + data any) (*http.Response, error) { + // 创建URL + u, err := url.Parse(urlStr) + if err != nil { + return nil, err + } + + // 添加查询参数 + query := u.Query() + for k, v := range params { + query.Set(k, v) + } + u.RawQuery = query.Encode() + + // 将数据编码为JSON + buf := new(bytes.Buffer) + if data != nil { + b, err := json.Marshal(data) + if err != nil { + return nil, err + } + buf = bytes.NewBuffer(b) + } + + // 创建请求 + req, err := http.NewRequest(method, u.String(), buf) + + if err != nil { + return nil, err + } + + for k, v := range headers { + req.Header.Set(k, v) + } + + if data != nil { + req.Header.Set("Content-Type", "application/json") + } + + // 发送请求 + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // 返回响应,让调用者处理 + return resp, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/server.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/server.go new file mode 100644 index 000000000..8c14cd79d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/server.go @@ -0,0 +1,126 @@ +package utils + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "runtime" + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/mem" +) + +const ( + B = 1 + KB = 1024 * B + MB = 1024 * KB + GB = 1024 * MB +) + +type Server struct { + Os Os `json:"os"` + Cpu Cpu `json:"cpu"` + Ram Ram `json:"ram"` + Disk []Disk `json:"disk"` +} + +type Os struct { + GOOS string `json:"goos"` + NumCPU int `json:"numCpu"` + Compiler string `json:"compiler"` + GoVersion string `json:"goVersion"` + NumGoroutine int `json:"numGoroutine"` +} + +type Cpu struct { + Cpus []float64 `json:"cpus"` + Cores int `json:"cores"` +} + +type Ram struct { + UsedMB int `json:"usedMb"` + TotalMB int `json:"totalMb"` + UsedPercent int `json:"usedPercent"` +} + +type Disk struct { + MountPoint string `json:"mountPoint"` + UsedMB int `json:"usedMb"` + UsedGB int `json:"usedGb"` + TotalMB int `json:"totalMb"` + TotalGB int `json:"totalGb"` + UsedPercent int `json:"usedPercent"` +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: InitCPU +//@description: OS信息 +//@return: o Os, err error + +func InitOS() (o Os) { + o.GOOS = runtime.GOOS + o.NumCPU = runtime.NumCPU() + o.Compiler = runtime.Compiler + o.GoVersion = runtime.Version() + o.NumGoroutine = runtime.NumGoroutine() + return o +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: InitCPU +//@description: CPU信息 +//@return: c Cpu, err error + +func InitCPU() (c Cpu, err error) { + if cores, err := cpu.Counts(false); err != nil { + return c, err + } else { + c.Cores = cores + } + if cpus, err := cpu.Percent(time.Duration(200)*time.Millisecond, true); err != nil { + return c, err + } else { + c.Cpus = cpus + } + return c, nil +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: InitRAM +//@description: RAM信息 +//@return: r Ram, err error + +func InitRAM() (r Ram, err error) { + if u, err := mem.VirtualMemory(); err != nil { + return r, err + } else { + r.UsedMB = int(u.Used) / MB + r.TotalMB = int(u.Total) / MB + r.UsedPercent = int(u.UsedPercent) + } + return r, nil +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@function: InitDisk +//@description: 硬盘信息 +//@return: d Disk, err error + +func InitDisk() (d []Disk, err error) { + for i := range global.GVA_CONFIG.DiskList { + mp := global.GVA_CONFIG.DiskList[i].MountPoint + if u, err := disk.Usage(mp); err != nil { + return d, err + } else { + d = append(d, Disk{ + MountPoint: mp, + UsedMB: int(u.Used) / MB, + UsedGB: int(u.Used) / GB, + TotalMB: int(u.Total) / MB, + TotalGB: int(u.Total) / GB, + UsedPercent: int(u.UsedPercent), + }) + } + } + return d, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/timer/timed_task.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/timer/timed_task.go new file mode 100644 index 000000000..b8c4edfab --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/timer/timed_task.go @@ -0,0 +1,229 @@ +package timer + +import ( + "github.com/robfig/cron/v3" + "sync" +) + +type Timer interface { + // 寻找所有Cron + FindCronList() map[string]*taskManager + // 添加Task 方法形式以秒的形式加入 + AddTaskByFuncWithSecond(cronName string, spec string, fun func(), taskName string, option ...cron.Option) (cron.EntryID, error) // 添加Task Func以秒的形式加入 + // 添加Task 接口形式以秒的形式加入 + AddTaskByJobWithSeconds(cronName string, spec string, job interface{ Run() }, taskName string, option ...cron.Option) (cron.EntryID, error) + // 通过函数的方法添加任务 + AddTaskByFunc(cronName string, spec string, task func(), taskName string, option ...cron.Option) (cron.EntryID, error) + // 通过接口的方法添加任务 要实现一个带有 Run方法的接口触发 + AddTaskByJob(cronName string, spec string, job interface{ Run() }, taskName string, option ...cron.Option) (cron.EntryID, error) + // 获取对应taskName的cron 可能会为空 + FindCron(cronName string) (*taskManager, bool) + // 指定cron开始执行 + StartCron(cronName string) + // 指定cron停止执行 + StopCron(cronName string) + // 查找指定cron下的指定task + FindTask(cronName string, taskName string) (*task, bool) + // 根据id删除指定cron下的指定task + RemoveTask(cronName string, id int) + // 根据taskName删除指定cron下的指定task + RemoveTaskByName(cronName string, taskName string) + // 清理掉指定cronName + Clear(cronName string) + // 停止所有的cron + Close() +} + +type task struct { + EntryID cron.EntryID + Spec string + TaskName string +} + +type taskManager struct { + corn *cron.Cron + tasks map[cron.EntryID]*task +} + +// timer 定时任务管理 +type timer struct { + cronList map[string]*taskManager + sync.Mutex +} + +// AddTaskByFunc 通过函数的方法添加任务 +func (t *timer) AddTaskByFunc(cronName string, spec string, fun func(), taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddFunc(spec, fun) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryID: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// AddTaskByFuncWithSeconds 通过函数的方法使用WithSeconds添加任务 +func (t *timer) AddTaskByFuncWithSecond(cronName string, spec string, fun func(), taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + option = append(option, cron.WithSeconds()) + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddFunc(spec, fun) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryID: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// AddTaskByJob 通过接口的方法添加任务 +func (t *timer) AddTaskByJob(cronName string, spec string, job interface{ Run() }, taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddJob(spec, job) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryID: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// AddTaskByJobWithSeconds 通过接口的方法添加任务 +func (t *timer) AddTaskByJobWithSeconds(cronName string, spec string, job interface{ Run() }, taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + option = append(option, cron.WithSeconds()) + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddJob(spec, job) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryID: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// FindTask 获取对应cronName的cron 可能会为空 +func (t *timer) FindCron(cronName string) (*taskManager, bool) { + t.Lock() + defer t.Unlock() + v, ok := t.cronList[cronName] + return v, ok +} + +// FindTask 获取对应cronName的cron 可能会为空 +func (t *timer) FindTask(cronName string, taskName string) (*task, bool) { + t.Lock() + defer t.Unlock() + v, ok := t.cronList[cronName] + if !ok { + return nil, ok + } + for _, t2 := range v.tasks { + if t2.TaskName == taskName { + return t2, true + } + } + return nil, false +} + +// FindCronList 获取所有的任务列表 +func (t *timer) FindCronList() map[string]*taskManager { + t.Lock() + defer t.Unlock() + return t.cronList +} + +// StartCron 开始任务 +func (t *timer) StartCron(cronName string) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Start() + } +} + +// StopCron 停止任务 +func (t *timer) StopCron(cronName string) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Stop() + } +} + +// Remove 从cronName 删除指定任务 +func (t *timer) RemoveTask(cronName string, id int) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Remove(cron.EntryID(id)) + delete(v.tasks, cron.EntryID(id)) + } +} + +// RemoveTaskByName 从cronName 使用taskName 删除指定任务 +func (t *timer) RemoveTaskByName(cronName string, taskName string) { + fTask, ok := t.FindTask(cronName, taskName) + if !ok { + return + } + t.RemoveTask(cronName, int(fTask.EntryID)) +} + +// Clear 清除任务 +func (t *timer) Clear(cronName string) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Stop() + delete(t.cronList, cronName) + } +} + +// Close 释放资源 +func (t *timer) Close() { + t.Lock() + defer t.Unlock() + for _, v := range t.cronList { + v.corn.Stop() + } +} + +func NewTimerTask() Timer { + return &timer{cronList: make(map[string]*taskManager)} +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/timer/timed_task_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/timer/timed_task_test.go new file mode 100644 index 000000000..9f2c02c0b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/timer/timed_task_test.go @@ -0,0 +1,72 @@ +package timer + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var job = mockJob{} + +type mockJob struct{} + +func (job mockJob) Run() { + mockFunc() +} + +func mockFunc() { + time.Sleep(time.Second) + fmt.Println("1s...") +} + +func TestNewTimerTask(t *testing.T) { + tm := NewTimerTask() + _tm := tm.(*timer) + + { + _, err := tm.AddTaskByFunc("func", "@every 1s", mockFunc, "测试mockfunc") + assert.Nil(t, err) + _, ok := _tm.cronList["func"] + if !ok { + t.Error("no find func") + } + } + + { + _, err := tm.AddTaskByJob("job", "@every 1s", job, "测试job mockfunc") + assert.Nil(t, err) + _, ok := _tm.cronList["job"] + if !ok { + t.Error("no find job") + } + } + + { + _, ok := tm.FindCron("func") + if !ok { + t.Error("no find func") + } + _, ok = tm.FindCron("job") + if !ok { + t.Error("no find job") + } + _, ok = tm.FindCron("none") + if ok { + t.Error("find none") + } + } + { + tm.Clear("func") + _, ok := tm.FindCron("func") + if ok { + t.Error("find func") + } + } + { + a := tm.FindCronList() + b, c := tm.FindCron("job") + fmt.Println(a, b, c) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/aliyun_oss.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/aliyun_oss.go new file mode 100644 index 000000000..cf7410161 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/aliyun_oss.go @@ -0,0 +1,75 @@ +package upload + +import ( + "errors" + "mime/multipart" + "time" + + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "go.uber.org/zap" +) + +type AliyunOSS struct{} + +func (*AliyunOSS) UploadFile(file *multipart.FileHeader) (string, string, error) { + bucket, err := NewBucket() + if err != nil { + global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error())) + return "", "", errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error()) + } + + // 读取本地文件。 + f, openError := file.Open() + if openError != nil { + global.GVA_LOG.Error("function file.Open() Failed", zap.Any("err", openError.Error())) + return "", "", errors.New("function file.Open() Failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + // 上传阿里云路径 文件名格式 自己可以改 建议保证唯一性 + // yunFileTmpPath := filepath.Join("uploads", time.Now().Format("2006-01-02")) + "/" + file.Filename + yunFileTmpPath := global.GVA_CONFIG.AliyunOSS.BasePath + "/" + "uploads" + "/" + time.Now().Format("2006-01-02") + "/" + file.Filename + + // 上传文件流。 + err = bucket.PutObject(yunFileTmpPath, f) + if err != nil { + global.GVA_LOG.Error("function formUploader.Put() Failed", zap.Any("err", err.Error())) + return "", "", errors.New("function formUploader.Put() Failed, err:" + err.Error()) + } + + return global.GVA_CONFIG.AliyunOSS.BucketUrl + "/" + yunFileTmpPath, yunFileTmpPath, nil +} + +func (*AliyunOSS) DeleteFile(key string) error { + bucket, err := NewBucket() + if err != nil { + global.GVA_LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error())) + return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error()) + } + + // 删除单个文件。objectName表示删除OSS文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。 + // 如需删除文件夹,请将objectName设置为对应的文件夹名称。如果文件夹非空,则需要将文件夹下的所有object删除后才能删除该文件夹。 + err = bucket.DeleteObject(key) + if err != nil { + global.GVA_LOG.Error("function bucketManager.Delete() failed", zap.Any("err", err.Error())) + return errors.New("function bucketManager.Delete() failed, err:" + err.Error()) + } + + return nil +} + +func NewBucket() (*oss.Bucket, error) { + // 创建OSSClient实例。 + client, err := oss.New(global.GVA_CONFIG.AliyunOSS.Endpoint, global.GVA_CONFIG.AliyunOSS.AccessKeyId, global.GVA_CONFIG.AliyunOSS.AccessKeySecret) + if err != nil { + return nil, err + } + + // 获取存储空间。 + bucket, err := client.Bucket(global.GVA_CONFIG.AliyunOSS.BucketName) + if err != nil { + return nil, err + } + + return bucket, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/aws_s3.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/aws_s3.go new file mode 100644 index 000000000..342f9b8b4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/aws_s3.go @@ -0,0 +1,97 @@ +package upload + +import ( + "errors" + "fmt" + "mime/multipart" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "go.uber.org/zap" +) + +type AwsS3 struct{} + +//@author: [WqyJh](https://github.com/WqyJh) +//@object: *AwsS3 +//@function: UploadFile +//@description: Upload file to Aws S3 using aws-sdk-go. See https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/s3-example-basic-bucket-operations.html#s3-examples-bucket-ops-upload-file-to-bucket +//@param: file *multipart.FileHeader +//@return: string, string, error + +func (*AwsS3) UploadFile(file *multipart.FileHeader) (string, string, error) { + session := newSession() + uploader := s3manager.NewUploader(session) + + fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) + filename := global.GVA_CONFIG.AwsS3.PathPrefix + "/" + fileKey + f, openError := file.Open() + if openError != nil { + global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error())) + return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + + _, err := uploader.Upload(&s3manager.UploadInput{ + Bucket: aws.String(global.GVA_CONFIG.AwsS3.Bucket), + Key: aws.String(filename), + Body: f, + }) + if err != nil { + global.GVA_LOG.Error("function uploader.Upload() failed", zap.Any("err", err.Error())) + return "", "", err + } + + return global.GVA_CONFIG.AwsS3.BaseURL + "/" + filename, fileKey, nil +} + +//@author: [WqyJh](https://github.com/WqyJh) +//@object: *AwsS3 +//@function: DeleteFile +//@description: Delete file from Aws S3 using aws-sdk-go. See https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/s3-example-basic-bucket-operations.html#s3-examples-bucket-ops-delete-bucket-item +//@param: file *multipart.FileHeader +//@return: string, string, error + +func (*AwsS3) DeleteFile(key string) error { + session := newSession() + svc := s3.New(session) + filename := global.GVA_CONFIG.AwsS3.PathPrefix + "/" + key + bucket := global.GVA_CONFIG.AwsS3.Bucket + + _, err := svc.DeleteObject(&s3.DeleteObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(filename), + }) + if err != nil { + global.GVA_LOG.Error("function svc.DeleteObject() failed", zap.Any("err", err.Error())) + return errors.New("function svc.DeleteObject() failed, err:" + err.Error()) + } + + _ = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(filename), + }) + return nil +} + +// newSession Create S3 session +func newSession() *session.Session { + sess, _ := session.NewSession(&aws.Config{ + Region: aws.String(global.GVA_CONFIG.AwsS3.Region), + Endpoint: aws.String(global.GVA_CONFIG.AwsS3.Endpoint), //minio在这里设置地址,可以兼容 + S3ForcePathStyle: aws.Bool(global.GVA_CONFIG.AwsS3.S3ForcePathStyle), + DisableSSL: aws.Bool(global.GVA_CONFIG.AwsS3.DisableSSL), + Credentials: credentials.NewStaticCredentials( + global.GVA_CONFIG.AwsS3.SecretID, + global.GVA_CONFIG.AwsS3.SecretKey, + "", + ), + }) + return sess +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/cloudflare_r2.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/cloudflare_r2.go new file mode 100644 index 000000000..a68d212bc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/cloudflare_r2.go @@ -0,0 +1,85 @@ +package upload + +import ( + "errors" + "fmt" + "mime/multipart" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "go.uber.org/zap" +) + +type CloudflareR2 struct{} + +func (c *CloudflareR2) UploadFile(file *multipart.FileHeader) (fileUrl string, fileName string, err error) { + session := c.newSession() + client := s3manager.NewUploader(session) + + fileKey := fmt.Sprintf("%d_%s", time.Now().Unix(), file.Filename) + fileName = fmt.Sprintf("%s/%s", global.GVA_CONFIG.CloudflareR2.Path, fileKey) + f, openError := file.Open() + if openError != nil { + global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error())) + return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + + input := &s3manager.UploadInput{ + Bucket: aws.String(global.GVA_CONFIG.CloudflareR2.Bucket), + Key: aws.String(fileName), + Body: f, + } + + _, err = client.Upload(input) + if err != nil { + global.GVA_LOG.Error("function uploader.Upload() failed", zap.Any("err", err.Error())) + return "", "", err + } + + return fmt.Sprintf("%s/%s", global.GVA_CONFIG.CloudflareR2.BaseURL, + fileName), + fileKey, + nil +} + +func (c *CloudflareR2) DeleteFile(key string) error { + session := newSession() + svc := s3.New(session) + filename := global.GVA_CONFIG.CloudflareR2.Path + "/" + key + bucket := global.GVA_CONFIG.CloudflareR2.Bucket + + _, err := svc.DeleteObject(&s3.DeleteObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(filename), + }) + if err != nil { + global.GVA_LOG.Error("function svc.DeleteObject() failed", zap.Any("err", err.Error())) + return errors.New("function svc.DeleteObject() failed, err:" + err.Error()) + } + + _ = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(filename), + }) + return nil +} + +func (*CloudflareR2) newSession() *session.Session { + endpoint := fmt.Sprintf("%s.r2.cloudflarestorage.com", global.GVA_CONFIG.CloudflareR2.AccountID) + + return session.Must(session.NewSession(&aws.Config{ + Region: aws.String("auto"), + Endpoint: aws.String(endpoint), + Credentials: credentials.NewStaticCredentials( + global.GVA_CONFIG.CloudflareR2.AccessKeyID, + global.GVA_CONFIG.CloudflareR2.SecretAccessKey, + "", + ), + })) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/local.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/local.go new file mode 100644 index 000000000..bb0784995 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/local.go @@ -0,0 +1,109 @@ +package upload + +import ( + "errors" + "io" + "mime/multipart" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "go.uber.org/zap" +) + +var mu sync.Mutex + +type Local struct{} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [ccfish86](https://github.com/ccfish86) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@object: *Local +//@function: UploadFile +//@description: 上传文件 +//@param: file *multipart.FileHeader +//@return: string, string, error + +func (*Local) UploadFile(file *multipart.FileHeader) (string, string, error) { + // 读取文件后缀 + ext := filepath.Ext(file.Filename) + // 读取文件名并加密 + name := strings.TrimSuffix(file.Filename, ext) + name = utils.MD5V([]byte(name)) + // 拼接新文件名 + filename := name + "_" + time.Now().Format("20060102150405") + ext + // 尝试创建此路径 + mkdirErr := os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm) + if mkdirErr != nil { + global.GVA_LOG.Error("function os.MkdirAll() failed", zap.Any("err", mkdirErr.Error())) + return "", "", errors.New("function os.MkdirAll() failed, err:" + mkdirErr.Error()) + } + // 拼接路径和文件名 + p := global.GVA_CONFIG.Local.StorePath + "/" + filename + filepath := global.GVA_CONFIG.Local.Path + "/" + filename + + f, openError := file.Open() // 读取文件 + if openError != nil { + global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error())) + return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + + out, createErr := os.Create(p) + if createErr != nil { + global.GVA_LOG.Error("function os.Create() failed", zap.Any("err", createErr.Error())) + + return "", "", errors.New("function os.Create() failed, err:" + createErr.Error()) + } + defer out.Close() // 创建文件 defer 关闭 + + _, copyErr := io.Copy(out, f) // 传输(拷贝)文件 + if copyErr != nil { + global.GVA_LOG.Error("function io.Copy() failed", zap.Any("err", copyErr.Error())) + return "", "", errors.New("function io.Copy() failed, err:" + copyErr.Error()) + } + return filepath, filename, nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [ccfish86](https://github.com/ccfish86) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@object: *Local +//@function: DeleteFile +//@description: 删除文件 +//@param: key string +//@return: error + +func (*Local) DeleteFile(key string) error { + // 检查 key 是否为空 + if key == "" { + return errors.New("key不能为空") + } + + // 验证 key 是否包含非法字符或尝试访问存储路径之外的文件 + if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) { + return errors.New("非法的key") + } + + p := filepath.Join(global.GVA_CONFIG.Local.StorePath, key) + + // 检查文件是否存在 + if _, err := os.Stat(p); os.IsNotExist(err) { + return errors.New("文件不存在") + } + + // 使用文件锁防止并发删除 + mu.Lock() + defer mu.Unlock() + + err := os.Remove(p) + if err != nil { + return errors.New("文件删除失败: " + err.Error()) + } + + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/obs.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/obs.go new file mode 100644 index 000000000..70ff42e56 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/obs.go @@ -0,0 +1,69 @@ +package upload + +import ( + "mime/multipart" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/huaweicloud/huaweicloud-sdk-go-obs/obs" + "github.com/pkg/errors" +) + +var HuaWeiObs = new(Obs) + +type Obs struct{} + +func NewHuaWeiObsClient() (client *obs.ObsClient, err error) { + return obs.New(global.GVA_CONFIG.HuaWeiObs.AccessKey, global.GVA_CONFIG.HuaWeiObs.SecretKey, global.GVA_CONFIG.HuaWeiObs.Endpoint) +} + +func (o *Obs) UploadFile(file *multipart.FileHeader) (string, string, error) { + // var open multipart.File + open, err := file.Open() + if err != nil { + return "", "", err + } + defer open.Close() + filename := file.Filename + input := &obs.PutObjectInput{ + PutObjectBasicInput: obs.PutObjectBasicInput{ + ObjectOperationInput: obs.ObjectOperationInput{ + Bucket: global.GVA_CONFIG.HuaWeiObs.Bucket, + Key: filename, + }, + HttpHeader: obs.HttpHeader{ + ContentType: file.Header.Get("content-type"), + }, + }, + Body: open, + } + + var client *obs.ObsClient + client, err = NewHuaWeiObsClient() + if err != nil { + return "", "", errors.Wrap(err, "获取华为对象存储对象失败!") + } + + _, err = client.PutObject(input) + if err != nil { + return "", "", errors.Wrap(err, "文件上传失败!") + } + filepath := global.GVA_CONFIG.HuaWeiObs.Path + "/" + filename + return filepath, filename, err +} + +func (o *Obs) DeleteFile(key string) error { + client, err := NewHuaWeiObsClient() + if err != nil { + return errors.Wrap(err, "获取华为对象存储对象失败!") + } + input := &obs.DeleteObjectInput{ + Bucket: global.GVA_CONFIG.HuaWeiObs.Bucket, + Key: key, + } + var output *obs.DeleteObjectOutput + output, err = client.DeleteObject(input) + if err != nil { + return errors.Wrapf(err, "删除对象(%s)失败!, output: %v", key, output) + } + return nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/qiniu.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/qiniu.go new file mode 100644 index 000000000..f4287d590 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/qiniu.go @@ -0,0 +1,96 @@ +package upload + +import ( + "context" + "errors" + "fmt" + "mime/multipart" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/qiniu/go-sdk/v7/auth/qbox" + "github.com/qiniu/go-sdk/v7/storage" + "go.uber.org/zap" +) + +type Qiniu struct{} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [ccfish86](https://github.com/ccfish86) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@object: *Qiniu +//@function: UploadFile +//@description: 上传文件 +//@param: file *multipart.FileHeader +//@return: string, string, error + +func (*Qiniu) UploadFile(file *multipart.FileHeader) (string, string, error) { + putPolicy := storage.PutPolicy{Scope: global.GVA_CONFIG.Qiniu.Bucket} + mac := qbox.NewMac(global.GVA_CONFIG.Qiniu.AccessKey, global.GVA_CONFIG.Qiniu.SecretKey) + upToken := putPolicy.UploadToken(mac) + cfg := qiniuConfig() + formUploader := storage.NewFormUploader(cfg) + ret := storage.PutRet{} + putExtra := storage.PutExtra{Params: map[string]string{"x:name": "github logo"}} + + f, openError := file.Open() + if openError != nil { + global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error())) + + return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) // 文件名格式 自己可以改 建议保证唯一性 + putErr := formUploader.Put(context.Background(), &ret, upToken, fileKey, f, file.Size, &putExtra) + if putErr != nil { + global.GVA_LOG.Error("function formUploader.Put() failed", zap.Any("err", putErr.Error())) + return "", "", errors.New("function formUploader.Put() failed, err:" + putErr.Error()) + } + return global.GVA_CONFIG.Qiniu.ImgPath + "/" + ret.Key, ret.Key, nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@author: [ccfish86](https://github.com/ccfish86) +//@author: [SliverHorn](https://github.com/SliverHorn) +//@object: *Qiniu +//@function: DeleteFile +//@description: 删除文件 +//@param: key string +//@return: error + +func (*Qiniu) DeleteFile(key string) error { + mac := qbox.NewMac(global.GVA_CONFIG.Qiniu.AccessKey, global.GVA_CONFIG.Qiniu.SecretKey) + cfg := qiniuConfig() + bucketManager := storage.NewBucketManager(mac, cfg) + if err := bucketManager.Delete(global.GVA_CONFIG.Qiniu.Bucket, key); err != nil { + global.GVA_LOG.Error("function bucketManager.Delete() failed", zap.Any("err", err.Error())) + return errors.New("function bucketManager.Delete() failed, err:" + err.Error()) + } + return nil +} + +//@author: [SliverHorn](https://github.com/SliverHorn) +//@object: *Qiniu +//@function: qiniuConfig +//@description: 根据配置文件进行返回七牛云的配置 +//@return: *storage.Config + +func qiniuConfig() *storage.Config { + cfg := storage.Config{ + UseHTTPS: global.GVA_CONFIG.Qiniu.UseHTTPS, + UseCdnDomains: global.GVA_CONFIG.Qiniu.UseCdnDomains, + } + switch global.GVA_CONFIG.Qiniu.Zone { // 根据配置文件进行初始化空间对应的机房 + case "ZoneHuadong": + cfg.Zone = &storage.ZoneHuadong + case "ZoneHuabei": + cfg.Zone = &storage.ZoneHuabei + case "ZoneHuanan": + cfg.Zone = &storage.ZoneHuanan + case "ZoneBeimei": + cfg.Zone = &storage.ZoneBeimei + case "ZoneXinjiapo": + cfg.Zone = &storage.ZoneXinjiapo + } + return &cfg +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/tencent_cos.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/tencent_cos.go new file mode 100644 index 000000000..efb99d894 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/tencent_cos.go @@ -0,0 +1,61 @@ +package upload + +import ( + "context" + "errors" + "fmt" + "mime/multipart" + "net/http" + "net/url" + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + + "github.com/tencentyun/cos-go-sdk-v5" + "go.uber.org/zap" +) + +type TencentCOS struct{} + +// UploadFile upload file to COS +func (*TencentCOS) UploadFile(file *multipart.FileHeader) (string, string, error) { + client := NewClient() + f, openError := file.Open() + if openError != nil { + global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error())) + return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) + + _, err := client.Object.Put(context.Background(), global.GVA_CONFIG.TencentCOS.PathPrefix+"/"+fileKey, f, nil) + if err != nil { + panic(err) + } + return global.GVA_CONFIG.TencentCOS.BaseURL + "/" + global.GVA_CONFIG.TencentCOS.PathPrefix + "/" + fileKey, fileKey, nil +} + +// DeleteFile delete file form COS +func (*TencentCOS) DeleteFile(key string) error { + client := NewClient() + name := global.GVA_CONFIG.TencentCOS.PathPrefix + "/" + key + _, err := client.Object.Delete(context.Background(), name) + if err != nil { + global.GVA_LOG.Error("function bucketManager.Delete() failed", zap.Any("err", err.Error())) + return errors.New("function bucketManager.Delete() failed, err:" + err.Error()) + } + return nil +} + +// NewClient init COS client +func NewClient() *cos.Client { + urlStr, _ := url.Parse("https://" + global.GVA_CONFIG.TencentCOS.Bucket + ".cos." + global.GVA_CONFIG.TencentCOS.Region + ".myqcloud.com") + baseURL := &cos.BaseURL{BucketURL: urlStr} + client := cos.NewClient(baseURL, &http.Client{ + Transport: &cos.AuthorizationTransport{ + SecretID: global.GVA_CONFIG.TencentCOS.SecretID, + SecretKey: global.GVA_CONFIG.TencentCOS.SecretKey, + }, + }) + return client +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/upload.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/upload.go new file mode 100644 index 000000000..72fa44429 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/upload/upload.go @@ -0,0 +1,39 @@ +package upload + +import ( + "mime/multipart" + + "github.com/flipped-aurora/gin-vue-admin/server/global" +) + +// OSS 对象存储接口 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [ccfish86](https://github.com/ccfish86) +type OSS interface { + UploadFile(file *multipart.FileHeader) (string, string, error) + DeleteFile(key string) error +} + +// NewOss OSS的实例化方法 +// Author [SliverHorn](https://github.com/SliverHorn) +// Author [ccfish86](https://github.com/ccfish86) +func NewOss() OSS { + switch global.GVA_CONFIG.System.OssType { + case "local": + return &Local{} + case "qiniu": + return &Qiniu{} + case "tencent-cos": + return &TencentCOS{} + case "aliyun-oss": + return &AliyunOSS{} + case "huawei-obs": + return HuaWeiObs + case "aws-s3": + return &AwsS3{} + case "cloudflare-r2": + return &CloudflareR2{} + default: + return &Local{} + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/validator.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/validator.go new file mode 100644 index 000000000..a56dac030 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/validator.go @@ -0,0 +1,294 @@ +package utils + +import ( + "errors" + "reflect" + "regexp" + "strconv" + "strings" +) + +type Rules map[string][]string + +type RulesMap map[string]Rules + +var CustomizeMap = make(map[string]Rules) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: RegisterRule +//@description: 注册自定义规则方案建议在路由初始化层即注册 +//@param: key string, rule Rules +//@return: err error + +func RegisterRule(key string, rule Rules) (err error) { + if CustomizeMap[key] != nil { + return errors.New(key + "已注册,无法重复注册") + } else { + CustomizeMap[key] = rule + return nil + } +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: NotEmpty +//@description: 非空 不能为其对应类型的0值 +//@return: string + +func NotEmpty() string { + return "notEmpty" +} + +// @author: [zooqkl](https://github.com/zooqkl) +// @function: RegexpMatch +// @description: 正则校验 校验输入项是否满足正则表达式 +// @param: rule string +// @return: string + +func RegexpMatch(rule string) string { + return "regexp=" + rule +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Lt +//@description: 小于入参(<) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Lt(mark string) string { + return "lt=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Le +//@description: 小于等于入参(<=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Le(mark string) string { + return "le=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Eq +//@description: 等于入参(==) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Eq(mark string) string { + return "eq=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Ne +//@description: 不等于入参(!=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Ne(mark string) string { + return "ne=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Ge +//@description: 大于等于入参(>=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Ge(mark string) string { + return "ge=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Gt +//@description: 大于入参(>) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Gt(mark string) string { + return "gt=" + mark +} + +// +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Verify +//@description: 校验方法 +//@param: st interface{}, roleMap Rules(入参实例,规则map) +//@return: err error + +func Verify(st interface{}, roleMap Rules) (err error) { + compareMap := map[string]bool{ + "lt": true, + "le": true, + "eq": true, + "ne": true, + "ge": true, + "gt": true, + } + + typ := reflect.TypeOf(st) + val := reflect.ValueOf(st) // 获取reflect.Type类型 + + kd := val.Kind() // 获取到st对应的类别 + if kd != reflect.Struct { + return errors.New("expect struct") + } + num := val.NumField() + // 遍历结构体的所有字段 + for i := 0; i < num; i++ { + tagVal := typ.Field(i) + val := val.Field(i) + if tagVal.Type.Kind() == reflect.Struct { + if err = Verify(val.Interface(), roleMap); err != nil { + return err + } + } + if len(roleMap[tagVal.Name]) > 0 { + for _, v := range roleMap[tagVal.Name] { + switch { + case v == "notEmpty": + if isBlank(val) { + return errors.New(tagVal.Name + "值不能为空") + } + case strings.Split(v, "=")[0] == "regexp": + if !regexpMatch(strings.Split(v, "=")[1], val.String()) { + return errors.New(tagVal.Name + "格式校验不通过") + } + case compareMap[strings.Split(v, "=")[0]]: + if !compareVerify(val, v) { + return errors.New(tagVal.Name + "长度或值不在合法范围," + v) + } + } + } + } + } + return nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: compareVerify +//@description: 长度和数字的校验方法 根据类型自动校验 +//@param: value reflect.Value, VerifyStr string +//@return: bool + +func compareVerify(value reflect.Value, VerifyStr string) bool { + switch value.Kind() { + case reflect.String: + return compare(len([]rune(value.String())), VerifyStr) + case reflect.Slice, reflect.Array: + return compare(value.Len(), VerifyStr) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return compare(value.Uint(), VerifyStr) + case reflect.Float32, reflect.Float64: + return compare(value.Float(), VerifyStr) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return compare(value.Int(), VerifyStr) + default: + return false + } +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: isBlank +//@description: 非空校验 +//@param: value reflect.Value +//@return: bool + +func isBlank(value reflect.Value) bool { + switch value.Kind() { + case reflect.String, reflect.Slice: + return value.Len() == 0 + case reflect.Bool: + return !value.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return value.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return value.Uint() == 0 + case reflect.Float32, reflect.Float64: + return value.Float() == 0 + case reflect.Interface, reflect.Ptr: + return value.IsNil() + } + return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface()) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: compare +//@description: 比较函数 +//@param: value interface{}, VerifyStr string +//@return: bool + +func compare(value interface{}, VerifyStr string) bool { + VerifyStrArr := strings.Split(VerifyStr, "=") + val := reflect.ValueOf(value) + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + VInt, VErr := strconv.ParseInt(VerifyStrArr[1], 10, 64) + if VErr != nil { + return false + } + switch { + case VerifyStrArr[0] == "lt": + return val.Int() < VInt + case VerifyStrArr[0] == "le": + return val.Int() <= VInt + case VerifyStrArr[0] == "eq": + return val.Int() == VInt + case VerifyStrArr[0] == "ne": + return val.Int() != VInt + case VerifyStrArr[0] == "ge": + return val.Int() >= VInt + case VerifyStrArr[0] == "gt": + return val.Int() > VInt + default: + return false + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + VInt, VErr := strconv.Atoi(VerifyStrArr[1]) + if VErr != nil { + return false + } + switch { + case VerifyStrArr[0] == "lt": + return val.Uint() < uint64(VInt) + case VerifyStrArr[0] == "le": + return val.Uint() <= uint64(VInt) + case VerifyStrArr[0] == "eq": + return val.Uint() == uint64(VInt) + case VerifyStrArr[0] == "ne": + return val.Uint() != uint64(VInt) + case VerifyStrArr[0] == "ge": + return val.Uint() >= uint64(VInt) + case VerifyStrArr[0] == "gt": + return val.Uint() > uint64(VInt) + default: + return false + } + case reflect.Float32, reflect.Float64: + VFloat, VErr := strconv.ParseFloat(VerifyStrArr[1], 64) + if VErr != nil { + return false + } + switch { + case VerifyStrArr[0] == "lt": + return val.Float() < VFloat + case VerifyStrArr[0] == "le": + return val.Float() <= VFloat + case VerifyStrArr[0] == "eq": + return val.Float() == VFloat + case VerifyStrArr[0] == "ne": + return val.Float() != VFloat + case VerifyStrArr[0] == "ge": + return val.Float() >= VFloat + case VerifyStrArr[0] == "gt": + return val.Float() > VFloat + default: + return false + } + default: + return false + } +} + +func regexpMatch(rule, matchStr string) bool { + return regexp.MustCompile(rule).MatchString(matchStr) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/validator_test.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/validator_test.go new file mode 100644 index 000000000..bdacb8b35 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/validator_test.go @@ -0,0 +1,37 @@ +package utils + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "testing" +) + +type PageInfoTest struct { + PageInfo request.PageInfo + Name string +} + +func TestVerify(t *testing.T) { + PageInfoVerify := Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}, "Name": {NotEmpty()}} + var testInfo PageInfoTest + testInfo.Name = "test" + testInfo.PageInfo.Page = 0 + testInfo.PageInfo.PageSize = 0 + err := Verify(testInfo, PageInfoVerify) + if err == nil { + t.Error("校验失败,未能捕捉0值") + } + testInfo.Name = "" + testInfo.PageInfo.Page = 1 + testInfo.PageInfo.PageSize = 10 + err = Verify(testInfo, PageInfoVerify) + if err == nil { + t.Error("校验失败,未能正常检测name为空") + } + testInfo.Name = "test" + testInfo.PageInfo.Page = 1 + testInfo.PageInfo.PageSize = 10 + err = Verify(testInfo, PageInfoVerify) + if err != nil { + t.Error("校验失败,未能正常通过检测") + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/verify.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/verify.go new file mode 100644 index 000000000..43a86725f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/verify.go @@ -0,0 +1,19 @@ +package utils + +var ( + IdVerify = Rules{"ID": []string{NotEmpty()}} + ApiVerify = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}} + MenuVerify = Rules{"Path": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}} + MenuMetaVerify = Rules{"Title": {NotEmpty()}} + LoginVerify = Rules{"CaptchaId": {NotEmpty()}, "Username": {NotEmpty()}, "Password": {NotEmpty()}} + RegisterVerify = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}} + PageInfoVerify = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}} + CustomerVerify = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}} + AutoCodeVerify = Rules{"Abbreviation": {NotEmpty()}, "StructName": {NotEmpty()}, "PackageName": {NotEmpty()}} + AutoPackageVerify = Rules{"PackageName": {NotEmpty()}} + AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}} + AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}} + OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}} + ChangePasswordVerify = Rules{"Password": {NotEmpty()}, "NewPassword": {NotEmpty()}} + SetUserAuthorityVerify = Rules{"AuthorityId": {NotEmpty()}} +) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/zip.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/zip.go new file mode 100644 index 000000000..bee0a0bf4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/server/utils/zip.go @@ -0,0 +1,53 @@ +package utils + +import ( + "archive/zip" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +// 解压 +func Unzip(zipFile string, destDir string) ([]string, error) { + zipReader, err := zip.OpenReader(zipFile) + var paths []string + if err != nil { + return []string{}, err + } + defer zipReader.Close() + + for _, f := range zipReader.File { + if strings.Index(f.Name, "..") > -1 { + return []string{}, fmt.Errorf("%s 文件名不合法", f.Name) + } + fpath := filepath.Join(destDir, f.Name) + paths = append(paths, fpath) + if f.FileInfo().IsDir() { + os.MkdirAll(fpath, os.ModePerm) + } else { + if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return []string{}, err + } + + inFile, err := f.Open() + if err != nil { + return []string{}, err + } + defer inFile.Close() + + outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return []string{}, err + } + defer outFile.Close() + + _, err = io.Copy(outFile, inFile) + if err != nil { + return []string{}, err + } + } + } + return paths, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/LLM.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/LLM.go new file mode 100644 index 000000000..0defdb0f2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/LLM.go @@ -0,0 +1,5 @@ +package supervisor + +func PromptLLM(prompt string) (string, error) { + return "", nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/jury.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/jury.go new file mode 100644 index 000000000..bc111dde2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/jury.go @@ -0,0 +1,513 @@ +package supervisor + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "hufu/config" + "hufu/contract/Decision" + "hufu/contract/KeyShare" + "hufu/utils" + "io" + "log" + "math/big" + "net/http" + "strconv" + "strings" + + "github.com/FISCO-BCOS/go-sdk/v3/client" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +type Jury struct { + Nodes []*Node + KeyShareContract string + DecisionContract string +} + +type Node struct { + NodeID string + PublicKey string + PrivateKey string +} + +var JuryInstance *Jury + +func InitJury() { + // keys + juryPrivateKeys, juryPublicKeys := config.ReadJuryKeys() + nodes := make([]*Node, 5) + for i := 0; i < 5; i++ { + nodes[i] = &Node{ + NodeID: fmt.Sprintf("node%d", i), + PublicKey: juryPublicKeys[i], + PrivateKey: juryPrivateKeys[i], + } + } + JuryInstance = &Jury{ + Nodes: nodes, + KeyShareContract: config.GlobalConfig.Contract.KeyShareContract, + DecisionContract: config.GlobalConfig.Contract.DecisionContract, + } + log.Println(JuryInstance.KeyShareContract) + log.Println(JuryInstance.DecisionContract) +} + +// 处理监管机构的请求 +func (j *Jury) HandleRegulatoryRequest(evidence string) (map[string]bool, error) { + // 存储同意提供密钥的节点的密钥碎片 + res := make(map[string]bool) + // 每个节点独立判断证据是否合法 + for _, node := range j.Nodes { + approved, err := node.EvaluateEvidence(evidence) + res[node.NodeID] = approved + if err != nil { + log.Printf("Node evaluation failed: %v", err) + continue + } + + if approved { + // 记录操作到区块链 + if err := node.RecordDecision(evidence); err != nil { + log.Printf("Failed to record decision: %v", err) + continue + } + } + } + return res, nil +} + +// 节点评估证据 +func (n *Node) EvaluateEvidence(evidence string) (bool, error) { + config := config.ReadConfig(n.PrivateKey) + client, err := client.DialContext(context.Background(), config) + if err != nil { + return false, err + } + + contractAddr := common.HexToAddress(JuryInstance.DecisionContract) + instance, err := Decision.NewDecisionStorage(contractAddr, client) + if err != nil { + return false, err + } + + session := &Decision.DecisionStorageSession{ + Contract: instance, + CallOpts: *client.GetCallOpts(), + TransactOpts: *client.GetTransactOpts(), + } + + // 调用智能合约验证证据 + approved, err := session.VerifyEvidence(evidence) + if err != nil { + return false, err + } + + return approved, nil +} + +// 记录决定到区块链 +func (n *Node) RecordDecision(evidence string) error { + config := config.ReadConfig(n.PrivateKey) + client, err := client.DialContext(context.Background(), config) + if err != nil { + return err + } + + contractAddr := common.HexToAddress(JuryInstance.DecisionContract) + instance, err := Decision.NewDecisionStorage(contractAddr, client) + if err != nil { + return err + } + + session := &Decision.DecisionStorageSession{ + Contract: instance, + CallOpts: *client.GetCallOpts(), + TransactOpts: *client.GetTransactOpts(), + } + + _, _, _, err = session.RecordDecision(evidence, n.NodeID, true) + if err != nil { + return err + } + + // log.Println(receipt) + + return nil +} + +// 存储加密后的密钥碎片 +func (n *Node) StoreEncryptedKeyShare(id, name, encryptedPart string) error { + // 初始化客户端连接 + config := config.ReadConfig(n.PrivateKey) + client, err := client.DialContext(context.Background(), config) + if err != nil { + return fmt.Errorf("failed to connect to client: %v", err) + } + + // 获取KeyShare合约实例 + contractAddr := common.HexToAddress(JuryInstance.KeyShareContract) + instance, err := KeyShare.NewKeyShare(contractAddr, client) + if err != nil { + return fmt.Errorf("failed to instantiate contract: %v", err) + } + + // 创建合约会话 + session := &KeyShare.KeyShareSession{ + Contract: instance, + CallOpts: *client.GetCallOpts(), + TransactOpts: *client.GetTransactOpts(), + } + + // 调用合约存储密钥碎片 + _, _, _, err = session.Insert(id, name, encryptedPart) + return err +} + +// 获取密钥碎片 +func (n *Node) GetDecryptedKeyShare(id string) (string, error) { + config := config.ReadConfig(n.PrivateKey) + client, err := client.DialContext(context.Background(), config) + if err != nil { + return "", err + } + + contractAddr := common.HexToAddress(JuryInstance.KeyShareContract) + instance, err := KeyShare.NewKeyShare(contractAddr, client) + if err != nil { + return "", err + } + + session := &KeyShare.KeyShareSession{ + Contract: instance, + CallOpts: *client.GetCallOpts(), + } + + _, encryptedPart, err := session.Select(id) + if err != nil { + return "", err + } + + decryptedPart, err := utils.DecryptData(encryptedPart, n.PrivateKey) + if err != nil { + return "", err + } + + return decryptedPart, nil +} + +// 获取决策 +func GetDecision() ([]string, error) { + // 获取决策合约实例 + config := config.ReadConfig(JuryInstance.Nodes[0].PrivateKey) + client, err := client.DialContext(context.Background(), config) + if err != nil { + return nil, err + } + + contractAddr := common.HexToAddress(JuryInstance.DecisionContract) + instance, err := Decision.NewDecisionStorage(contractAddr, client) + if err != nil { + return nil, err + } + + session := &Decision.DecisionStorageSession{ + Contract: instance, + CallOpts: *client.GetCallOpts(), + TransactOpts: *client.GetTransactOpts(), + } + + // 调用合约获取决策 + count, err := session.GetDecisionCount() + if err != nil { + return nil, err + } + res := make([]string, count.Int64()) + for i := int64(0); i < count.Int64(); i++ { + decision, err := session.GetDecision(big.NewInt(i)) + if err != nil { + return nil, err + } + res[i] = decision.Evidence + "," + decision.NodeID + "," + decision.Timestamp.String() + "," + strconv.FormatBool(decision.Approved) + } + return res, nil +} + +// 添加这些结构体用于解析返回数据 +type EventResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data []EventData `json:"data"` + TotalCount int `json:"totalCount"` +} + +type EventData struct { + Log EventLog `json:"log"` +} + +type EventLog struct { + LogIndex int `json:"logIndex"` + TransactionIndex int `json:"transactionIndex"` + TransactionHash string `json:"transactionHash"` + BlockNumber int `json:"blockNumber"` + Address string `json:"address"` + Data string `json:"data"` + Topics []string `json:"topics"` +} + +func GetEvent() (EventResponse, error) { + url := "http://localhost:5000/mgr/WeBASE-Node-Manager/event/eventLogs/list" + + var requestBody = `{ + "groupId": "group0", + "contractAbi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "DecisionRecorded", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "decisions", + "outputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getDecision", + "outputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDecisionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "recordDecision", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + } + ], + "name": "verifyEvidence", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "contractAddress": "0x4721d1a77e0e76851d460073e64ea06d9c104194", + "fromBlock": 1, + "toBlock": -1, + "topics": { + "eventName": "DecisionRecorded(string,string,uint256,bool)" + } + }` + + var jsonStr = []byte(requestBody) + // 创建请求 + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) + if err != nil { + fmt.Println("Error creating request:", err) + return EventResponse{}, err + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorizationtoken", config.GlobalConfig.Fisco.Authorizationtoken) + req.Header.Set("referer", "http://45.8.113.140:5000/") + req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36") + + // 发送请求 + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error sending request:", err) + return EventResponse{}, err + } + defer resp.Body.Close() + + // 输出响应 + fmt.Println("Response status:", resp.Status) + + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + return EventResponse{}, err + } + + var response EventResponse + if err := json.Unmarshal(body, &response); err != nil { + fmt.Printf("Error parsing response: %v\n", err) + return EventResponse{}, err + } + + for i := 0; i < response.TotalCount; i++ { + data, _ := ParaseData(response.Data[i].Log.Data) + response.Data[i].Log.Data = data + } + + if response.Code != 0 { + fmt.Printf("API returned error: %s\n", response.Message) + return EventResponse{}, fmt.Errorf(response.Message) + } + + return response, nil +} + +func ParaseData(data string) (string, error) { + const abiJSON = `[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"evidence","type":"string"},{"indexed":false,"internalType":"string","name":"nodeID","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"DecisionRecorded","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"decisions","outputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"approved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getDecision","outputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"approved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDecisionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"evidence","type":"string"},{"internalType":"string","name":"nodeID","type":"string"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"recordDecision","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"evidence","type":"string"}],"name":"verifyEvidence","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]` + + parsedABI, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + log.Fatalf("Failed to parse ABI: %v", err) + } + + dataBytes, err := hexutil.Decode(data) + if err != nil { + log.Fatalf("Failed to decode data: %v", err) + } + + // 解码数据 + var Evidence string + var NodeID string + var Timestamp *big.Int + var Approved bool + err = parsedABI.UnpackIntoInterface(&[]interface{}{&Evidence, &NodeID, &Timestamp, &Approved}, "DecisionRecorded", dataBytes) + if err != nil { + log.Fatalf("Failed to unpack data: %v", err) + } + + res := []string{Evidence, NodeID, Timestamp.String(), strconv.FormatBool(Approved)} + + return strings.Join(res, ","), nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/palyload.json b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/palyload.json new file mode 100644 index 000000000..a7877f376 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/supervisor/palyload.json @@ -0,0 +1,171 @@ +{ + "groupId": "group0", + "contractAbi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "DecisionRecorded", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "decisions", + "outputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getDecision", + "outputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDecisionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + }, + { + "internalType": "string", + "name": "nodeID", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "recordDecision", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "evidence", + "type": "string" + } + ], + "name": "verifyEvidence", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "contractAddress": "0x33e56a083e135936c1144960a708c43a661706c0", + "fromBlock": 1, + "toBlock": -1, + "topics": { + "eventName": "DecisionRecorded(string,string,uint256,bool)" + } + } \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/utils/crypto.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/utils/crypto.go new file mode 100644 index 000000000..01dd6beaf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/utils/crypto.go @@ -0,0 +1,97 @@ +package utils + +import ( + "crypto/rand" + "crypto/rsa" + "encoding/hex" + "math/big" + "strings" +) + +// RSADecryptWithHexKey 使用十六进制私钥进行RSA解密 +func RSADecryptWithHexKey(encryptedData string, hexPrivateKey string, hexPublicKey string) (string, error) { + // 解码 + ciphertext, err := hex.DecodeString(encryptedData) + if err != nil { + return "", err + } + + // 转换十六进制私钥 + privateKey, err := hexToPrivateKey(hexPrivateKey, hexPublicKey) + if err != nil { + return "", err + } + + // 解密 + plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext) + if err != nil { + return "", err + } + + return string(plaintext), nil +} + +// RSAEncryptWithHexKey 使用十六进制公钥进行RSA加密 +func RSAEncryptWithHexKey(data string, hexPublicKey string) (string, error) { + // 转换十六进制公钥 + publicKey, err := hexToPublicKey(hexPublicKey) + if err != nil { + return "", err + } + + // 加密 + ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, []byte(data)) + if err != nil { + return "", err + } + + // 返回十六进制字符串 + return hex.EncodeToString(ciphertext), nil +} + +// hexToPublicKey 将十六进制字符串转换为RSA公钥 +func hexToPublicKey(hexStr string) (*rsa.PublicKey, error) { + hexStr = strings.ReplaceAll(hexStr, " ", "") + + // 解码十六进制字符串 + modBytes, err := hex.DecodeString(hexStr) + if err != nil { + return nil, err + } + + // 创建公钥 + pub := &rsa.PublicKey{ + N: new(big.Int).SetBytes(modBytes), + E: 65537, // 通常使用65537作为公钥指数 + } + + return pub, nil +} + +// hexToPrivateKey 将十六进制字符串转换为RSA私钥 +func hexToPrivateKey(hexPriKey string, hexPubKey string) (*rsa.PrivateKey, error) { + hexPriKey = strings.ReplaceAll(hexPriKey, " ", "") + hexPubKey = strings.ReplaceAll(hexPubKey, " ", "") + + // 解码十六进制字符串 + privBytes, err := hex.DecodeString(hexPriKey) + if err != nil { + return nil, err + } + + pubBytes, err := hex.DecodeString(hexPubKey) + if err != nil { + return nil, err + } + + // 创建私钥结构 + priv := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: new(big.Int).SetBytes(pubBytes), + E: 65537, + }, + D: new(big.Int).SetBytes(privBytes), + } + + return priv, nil +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/utils/utils.go b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/utils/utils.go new file mode 100644 index 000000000..c74ca546f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/utils/utils.go @@ -0,0 +1,297 @@ +package utils + +import ( + "bytes" + "crypto/ecdsa" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "log" + "math/big" + "net/http" + "strconv" + "time" + + "github.com/SSSaaS/sssa-golang" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "golang.org/x/net/proxy" +) + +const ( + MINIMUM = 3 // 最小份额数 + SHARES5 = 5 // 总份额数 +) + +const SupervisorPrivateKey = "43878f814c6753e43c1bd91db187e9399551e50876b7d24f7aba2cc467f88458" +const SupervisorPublicKey = "066583fe9369c70280b2af181e9b6d87eb63848f7af4ac1444dcc774e11805630dfc07918bcd80803a38f77f4b6f415e1d4e2596a79ecacc83f9a0ad95645326" + +const ProxyPublicKey = "066583fe9369c70280b2af181e9b6d87eb63848f7af4ac1444dcc774e11805630dfc07918bcd80803a38f77f4b6f415e1d4e2596a79ecacc83f9a0ad95645326" +const ProxyPrivateKey = "43878f814c6753e43c1bd91db187e9399551e50876b7d24f7aba2cc467f88458" + +// encryptData encrypts data using ECIES with the provided ECDSA public key. +func EncryptData(key string, data string) (string, error) { + // 将公钥字符串解码为字节数组 + publicKeyBytes, err := hex.DecodeString(key) + if err != nil { + return "", fmt.Errorf("failed to decode public key: %v", err) + } + + // 直接使用解码后的公钥字节 + // 加上0x04前缀,表示未压缩的公钥 + publicKeyBytes = append([]byte{0x04}, publicKeyBytes...) + publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) + if err != nil { + return "", fmt.Errorf("failed to unmarshal public key: %v", err) + } + + // Convert to ECIES public key + eciesPubKey := ecies.ImportECDSAPublic(publicKey) + + // Encrypt the data + ciphertext, err := ecies.Encrypt(rand.Reader, eciesPubKey, []byte(data), nil, nil) + if err != nil { + return "", fmt.Errorf("failed to encrypt data: %v", err) + } + + // Encode the ciphertext to base64 + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +// DecryptData decrypts data using ECIES with the provided ECDSA private key. +func DecryptData(rawData string, pk string) (string, error) { + // Decode the base64 encoded ciphertext + ciphertext, err := base64.StdEncoding.DecodeString(rawData) + if err != nil { + return "", fmt.Errorf("failed to decode ciphertext: %v", err) + } + + // Decode the private key from hex + privateKeyBytes, err := hex.DecodeString(pk) + if err != nil { + return "", fmt.Errorf("failed to decode private key: %v", err) + } + + // Parse the private key + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return "", fmt.Errorf("failed to parse private key: %v", err) + } + + // Convert to ECIES private key + eciesPrivKey := ecies.ImportECDSA(privateKey) + + // Decrypt the data + plaintext, err := eciesPrivKey.Decrypt(ciphertext, nil, nil) + if err != nil { + return "", fmt.Errorf("failed to decrypt data: %v", err) + } + + return string(plaintext), nil +} + +func GenerateHash(data string) string { + hash := sha256.New() + hash.Write([]byte(data)) + return hex.EncodeToString(hash.Sum(nil)) +} + +// generateKeys 生成随机密钥对 +func GenerateKeys() (privateKeyString, publicKeyString, address string) { + privateKey, err := crypto.GenerateKey() + if err != nil { + log.Fatal(err) + } + + privateKeyBytes := crypto.FromECDSA(privateKey) + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + + publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA) + + address = crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + + return hexutil.Encode(privateKeyBytes)[2:], hexutil.Encode(publicKeyBytes)[4:], address +} + +func GenerateRSAKey(walletID int) (privateKey, publicKey string) { + // 创建 SOCKS5 代理 + dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:1080", nil, proxy.Direct) + if err != nil { + log.Println(err) + return "", "" + } + + // 创建带有代理的 HTTP 客户端 + httpClient := &http.Client{ + Transport: &http.Transport{ + Dial: dialer.Dial, + }, + } + + // 准备请求 + jsonBody, _ := json.Marshal(map[string]int{"wallet_id": walletID}) + req, err := http.NewRequest("POST", "http://10.77.110.184:8082/api/create_wallet", bytes.NewBuffer(jsonBody)) + if err != nil { + log.Println("创建请求失败: ", err) + return "", "" + } + + // 设置 Content-Type + req.Header.Set("Content-Type", "application/json") + + // 发送请求 + resp, err := httpClient.Do(req) + if err != nil { + log.Println("发送请求失败: ", err) + return "", "" + } + defer resp.Body.Close() + + // 读取响应 + respBody, err := io.ReadAll(resp.Body) + if err != nil { + log.Println("读取响应失败: ", err) + return "", "" + } + + type KeyResponse struct { + Message string `json:"message"` + PublicKey string `json:"public_key"` + PrivateKey string `json:"private_key"` + } + + var keyResp KeyResponse + if err := json.Unmarshal(respBody, &keyResp); err != nil { + log.Println("解析响应失败: ", err) + return "", "" + } + log.Println("创建钱包响应: ", keyResp) + + if keyResp.Message != "wallet created successfully" { + log.Println("创建钱包失败: ", keyResp.Message) + return "", "" + } + + return keyResp.PrivateKey, keyResp.PublicKey +} + +// SharePrivateKey Shamir's secret sharing https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing +func SharePrivateKey(PrivateKey string) ([]string, error) { + result, err := sssa.Create(MINIMUM, SHARES5, PrivateKey) + return result, err +} + +func SignData(privateKey string, data string) (string, error) { + // Decode the private key from hex + privateKeyBytes, err := hex.DecodeString(privateKey) + if err != nil { + return "", fmt.Errorf("failed to decode private key: %v", err) + } + + // Parse the private key + ecPrivateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return "", fmt.Errorf("failed to parse private key: %v", err) + } + + // Generate the hash of the data + hash := sha256.New() + hash.Write([]byte(data)) + hashedData := hash.Sum(nil) + + // Sign the hashed data + r, s, err := ecdsa.Sign(rand.Reader, ecPrivateKey, hashedData) + if err != nil { + return "", fmt.Errorf("failed to sign data: %v", err) + } + + // Combine r and s into a single byte slice + rBytes := r.Bytes() + sBytes := s.Bytes() + signature := append(rBytes, sBytes...) + + // Encode the signature to hex + return hex.EncodeToString(signature), nil +} + +// VerifySignature verifies the ECDSA signature for the given data. +func VerifySignature(publicKey string, data string, signature string) (bool, error) { + // Decode the public key from hex + publicKeyBytes, err := hex.DecodeString(publicKey) + if err != nil { + return false, fmt.Errorf("failed to decode public key: %v", err) + } + + // Create an ECDSA public key from the bytes + ecPublicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) + if err != nil { + return false, fmt.Errorf("failed to unmarshal public key: %v", err) + } + + // Generate the hash of the data + hash := sha256.New() + hash.Write([]byte(data)) + hashedData := hash.Sum(nil) + + // Decode the signature from hex + sigBytes, err := hex.DecodeString(signature) + if err != nil { + return false, fmt.Errorf("failed to decode signature: %v", err) + } + + // Split the signature into r and s + r := new(big.Int).SetBytes(sigBytes[:len(sigBytes)/2]) + s := new(big.Int).SetBytes(sigBytes[len(sigBytes)/2:]) + + // Verify the signature + valid := ecdsa.Verify(ecPublicKey, hashedData, r, s) + return valid, nil +} + +func GenerateRandomNumber(length int) string { + // 创建一个包含所有数字的字符串 + const numbers = "0123456789" + + // 使用crypto/rand生成安全的随机数 + result := make([]byte, length) + for i := 0; i < length; i++ { + // 生成随机索引 + randomIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(numbers)))) + if err != nil { + // 如果出错,使用time作为后备方案 + randomIndex = big.NewInt(int64(time.Now().UnixNano() % int64(len(numbers)))) + } + result[i] = numbers[randomIndex.Int64()] + } + return string(result) +} + +// FormatFloat 格式化浮点数到指定小数位数 +func FormatFloat(num float64, decimal int) float64 { + // 将数字转为字符串,保留指定小数位 + format := fmt.Sprintf("%%.%df", decimal) + str := fmt.Sprintf(format, num) + + // 将字符串转回float64 + result, _ := strconv.ParseFloat(str, 64) + return result +} + +// StringToUint 将字符串转换为uint +func StringToUint(s string) uint { + i, err := strconv.ParseUint(s, 10, 32) + if err != nil { + return 0 + } + return uint(i) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.docker-compose/nginx/conf.d/my.conf b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.docker-compose/nginx/conf.d/my.conf new file mode 100644 index 000000000..9a1685dee --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.docker-compose/nginx/conf.d/my.conf @@ -0,0 +1,26 @@ +server { + listen 8080; + server_name localhost; + + #charset koi8-r; + #access_log logs/host.access.log main; + + location / { + root /usr/share/nginx/html; + add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + rewrite ^/api/(.*)$ /$1 break; #重写 + proxy_pass http://177.7.0.12:8888; # 设置代理服务器的协议和地址 + } + + location /api/swagger/index.html { + proxy_pass http://127.0.0.1:8888/swagger/index.html; + } + } \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.docker-compose/nginx/conf.d/nginx.conf b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.docker-compose/nginx/conf.d/nginx.conf new file mode 100644 index 000000000..29f68b81f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.docker-compose/nginx/conf.d/nginx.conf @@ -0,0 +1,32 @@ +server { + listen 80; + server_name localhost; + + #charset koi8-r; + #access_log logs/host.access.log main; + + location / { + root /usr/share/nginx/html/dist; + add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + rewrite ^/api/(.*)$ /$1 break; #重写 + proxy_pass http://127.0.0.1:8888; # 设置代理服务器的协议和地址 + } + location /form-generator { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://127.0.0.1:8888; + } + location /api/swagger/index.html { + proxy_pass http://127.0.0.1:8888/swagger/index.html; + } + } \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.dockerignore b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.dockerignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.dockerignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.env.development b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.env.development new file mode 100644 index 000000000..9634e65d6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.env.development @@ -0,0 +1,11 @@ +ENV = 'development' +VITE_CLI_PORT = 8080 +VITE_SERVER_PORT = 4000 +VITE_BASE_API = /api +VITE_FILE_API = /api +VITE_BASE_PATH = http://127.0.0.1 +VITE_POSITION = close +VITE_EDITOR = vscode +// VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm +// 如果使用docker-compose开发模式,设置为下面的地址或本机主机IP +//VITE_BASE_PATH = http://177.7.0.12 diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.env.production b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.env.production new file mode 100644 index 000000000..9345df2d8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.env.production @@ -0,0 +1,7 @@ +ENV = 'production' + +#下方为上线需要用到的程序代理前缀,一般用于nginx代理转发 +VITE_BASE_API = /api +VITE_FILE_API = /api +#下方修改为你的线上ip(如果需要在线使用表单构建工具时使用,其余情况无需使用以下环境变量) +VITE_BASE_PATH = https://demo.gin-vue-admin.com diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.eslintignore b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.eslintignore new file mode 100644 index 000000000..e6529fc09 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.eslintignore @@ -0,0 +1,4 @@ +build/*.js +src/assets +public +dist diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.eslintrc.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.eslintrc.js new file mode 100644 index 000000000..0821611b5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + root: true, + parserOptions: { + parser: '@babel/eslint-parser', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + rules: { + "vue/max-attributes-per-line" : 0, + "vue/no-v-model-argument" : 0 + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.gitignore b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.gitignore new file mode 100644 index 000000000..1a4abd90a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/.gitignore @@ -0,0 +1,5 @@ +node_modules/* +package-lock.json +yarn.lock +bun.lockb +config.yaml \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/README.md new file mode 100644 index 000000000..f662a7613 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/README.md @@ -0,0 +1,26 @@ + +## Project setup +``` +npm install +``` + +### Compiles and hot-reloads for development +``` +npm run serve +``` + +### Compiles and minifies for production +``` +npm run build +``` + +### Run your tests +``` +npm run test +``` + +### Lints and fixes files +``` +npm run lint +``` + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/babel.config.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/babel.config.js new file mode 100644 index 000000000..88029f087 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/babel.config.js @@ -0,0 +1,8 @@ +module.exports = { + presets: [ + + ], + 'plugins': [ + + ] +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/index.html b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/index.html new file mode 100644 index 000000000..4dd71e5a3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/index.html @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + +
+
+
+
+
+
系统正在加载中,请稍候...
+
+
+ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/jsconfig.json b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/jsconfig.json new file mode 100644 index 000000000..deaa520aa --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "exclude": ["node_modules", "dist"], + "include": ["src/**/*"] + } diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/limit.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/limit.js new file mode 100644 index 000000000..6ba9d4623 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/limit.js @@ -0,0 +1,37 @@ +// 运行项目前通过node执行此脚本 (此脚本与 node_modules 目录同级) +const fs = require('fs') +const path = require('path') +const wfPath = path.resolve(__dirname, './node_modules/.bin') + +fs.readdir(wfPath, (err, files) => { + if (err) { + console.log(err) + } else { + if (files.length !== 0) { + files.forEach((item) => { + if (item.split('.')[1] === 'cmd') { + replaceStr(`${wfPath}/${item}`, /"%_prog%"/, '%_prog%') + } + }) + } + } +}) + +// 参数:[文件路径、 需要修改的字符串、修改后的字符串] (替换对应文件内字符串的公共函数) +function replaceStr(filePath, sourceRegx, targetSrt) { + fs.readFile(filePath, (err, data) => { + if (err) { + console.log(err) + } else { + let str = data.toString() + str = str.replace(sourceRegx, targetSrt) + fs.writeFile(filePath, str, (err) => { + if (err) { + console.log(err) + } else { + console.log('\x1B[42m%s\x1B[0m', '文件修改成功') + } + }) + } + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/openDocument.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/openDocument.js new file mode 100644 index 000000000..ab52caf87 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/openDocument.js @@ -0,0 +1,15 @@ +var child_process = require('child_process') + +var url = '' +var cmd = '' +switch (process.platform) { + case 'win32': + cmd = 'start' + child_process.exec(cmd + ' ' + url) + break + + case 'darwin': + cmd = 'open' + child_process.exec(cmd + ' ' + url) + break +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/package.json b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/package.json new file mode 100644 index 000000000..06c927635 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/package.json @@ -0,0 +1,71 @@ +{ + "name": "gin-vue-admin", + "version": "2.7.6", + "private": true, + "scripts": { + "serve": "node openDocument.js && vite --host --mode development", + "build": "vite build --mode production", + "limit-build": "npm install increase-memory-limit-fixbug cross-env -g && npm run fix-memory-limit && node ./limit && npm run build", + "preview": "vite preview", + "fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "@form-create/designer": "^3.2.6", + "@form-create/element-ui": "^3.2.10", + "@vue-office/docx": "^1.6.2", + "@vue-office/excel": "^1.7.11", + "@vue-office/pdf": "^2.0.2", + "@vueuse/core": "^11.0.3", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", + "axios": "^1.7.7", + "chokidar": "^4.0.0", + "core-js": "^3.38.1", + "default-passive-events": "^2.0.0", + "echarts": "5.5.1", + "element-plus": "^2.8.4", + "highlight.js": "^11.10.0", + "js-cookie": "^3.0.5", + "jsencrypt": "^3.3.2", + "marked": "14.1.1", + "marked-highlight": "^2.1.4", + "mitt": "^3.0.1", + "nprogress": "^0.2.0", + "path": "^0.12.7", + "pinia": "^2.2.2", + "qs": "^6.13.0", + "screenfull": "^6.0.2", + "sortablejs": "^1.15.3", + "spark-md5": "^3.0.2", + "tailwindcss": "^3.4.10", + "vform3-builds": "^3.0.10", + "vite-auto-import-svg": "^1.1.0", + "vue": "^3.5.7", + "vue-echarts": "^7.0.3", + "vue-router": "^4.4.3", + "vuedraggable": "^4.1.0" + }, + "devDependencies": { + "@babel/eslint-parser": "^7.25.1", + "@vitejs/plugin-legacy": "^5.4.2", + "@vitejs/plugin-vue": "^5.1.3", + "@vue/cli-plugin-babel": "~5.0.8", + "@vue/cli-plugin-eslint": "~5.0.8", + "@vue/cli-plugin-router": "~5.0.8", + "@vue/cli-plugin-vuex": "~5.0.8", + "@vue/cli-service": "~5.0.8", + "@vue/compiler-sfc": "^3.5.1", + "babel-plugin-import": "^1.13.8", + "chalk": "^5.3.0", + "dotenv": "^16.4.5", + "eslint": "^9.9.1", + "eslint-plugin-vue": "^9.28.0", + "sass": "^1.78.0", + "terser": "^5.31.6", + "vite": "^5.4.3", + "vite-plugin-banner": "^0.8.0", + "vite-plugin-importer": "^0.2.5", + "vite-plugin-vue-devtools": "^7.4.4" + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/postcss.config.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/postcss.config.js new file mode 100644 index 000000000..33ad091d2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/public/favicon.ico b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/public/favicon.ico new file mode 100644 index 000000000..e56d4e361 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/public/favicon.ico differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/public/favicon2.ico b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/public/favicon2.ico new file mode 100644 index 000000000..ee520ce4b Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/public/favicon2.ico differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/App.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/App.vue new file mode 100644 index 000000000..c4e7535f6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/App.vue @@ -0,0 +1,41 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/api.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/api.js new file mode 100644 index 000000000..17147b04e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/api.js @@ -0,0 +1,179 @@ +import service from '@/utils/request' + +// @Tags api +// @Summary 分页获取角色列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body modelInterface.PageInfo true "分页获取用户列表" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /api/getApiList [post] +// { +// page int +// pageSize int +// } +export const getApiList = (data) => { + return service({ + url: '/api/getApiList', + method: 'post', + data + }) +} + +// @Tags Api +// @Summary 创建基础api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateApiParams true "创建api" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /api/createApi [post] +export const createApi = (data) => { + return service({ + url: '/api/createApi', + method: 'post', + data + }) +} + +// @Tags menu +// @Summary 根据id获取菜单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.GetById true "根据id获取菜单" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /menu/getApiById [post] +export const getApiById = (data) => { + return service({ + url: '/api/getApiById', + method: 'post', + data + }) +} + +// @Tags Api +// @Summary 更新api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateApiParams true "更新api" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /api/updateApi [post] +export const updateApi = (data) => { + return service({ + url: '/api/updateApi', + method: 'post', + data + }) +} + +// @Tags Api +// @Summary 更新api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateApiParams true "更新api" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /api/setAuthApi [post] +export const setAuthApi = (data) => { + return service({ + url: '/api/setAuthApi', + method: 'post', + data + }) +} + +// @Tags Api +// @Summary 获取所有的Api 不分页 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /api/getAllApis [post] +export const getAllApis = (data) => { + return service({ + url: '/api/getAllApis', + method: 'post', + data + }) +} + +// @Tags Api +// @Summary 删除指定api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body dbModel.Api true "删除api" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /api/deleteApi [post] +export const deleteApi = (data) => { + return service({ + url: '/api/deleteApi', + method: 'post', + data + }) +} + +// @Tags SysApi +// @Summary 删除选中Api +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "ID" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /api/deleteApisByIds [delete] +export const deleteApisByIds = (data) => { + return service({ + url: '/api/deleteApisByIds', + method: 'delete', + data + }) +} + +// FreshCasbin +// @Tags SysApi +// @Summary 刷新casbin缓存 +// @accept application/json +// @Produce application/json +// @Success 200 {object} response.Response{msg=string} "刷新成功" +// @Router /api/freshCasbin [get] +export const freshCasbin = () => { + return service({ + url: '/api/freshCasbin', + method: 'get' + }) +} + + +export const syncApi = () => { + return service({ + url: '/api/syncApi', + method: 'get' + }) +} + + +export const getApiGroups = () => { + return service({ + url: '/api/getApiGroups', + method: 'get' + }) +} + +export const ignoreApi = (data) => { + return service({ + url: '/api/ignoreApi', + method: 'post', + data + }) +} + + +export const enterSyncApi = (data) => { + return service({ + url: '/api/enterSyncApi', + method: 'post', + data + }) +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/authority.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/authority.js new file mode 100644 index 000000000..61b22067a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/authority.js @@ -0,0 +1,85 @@ +import service from '@/utils/request' +// @Router /authority/getAuthorityList [post] +export const getAuthorityList = (data) => { + return service({ + url: '/authority/getAuthorityList', + method: 'post', + data + }) +} + +// @Summary 删除角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body {authorityId uint} true "删除角色" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /authority/deleteAuthority [post] +export const deleteAuthority = (data) => { + return service({ + url: '/authority/deleteAuthority', + method: 'post', + data, + }) +} + +// @Summary 创建角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateAuthorityPatams true "创建角色" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /authority/createAuthority [post] +export const createAuthority = (data) => { + return service({ + url: '/authority/createAuthority', + method: 'post', + data + }) +} + +// @Tags authority +// @Summary 拷贝角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateAuthorityPatams true "拷贝角色" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"拷贝成功"}" +// @Router /authority/copyAuthority [post] +export const copyAuthority = (data) => { + return service({ + url: '/authority/copyAuthority', + method: 'post', + data + }) +} + +// @Summary 设置角色资源权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body sysModel.SysAuthority true "设置角色资源权限" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" +// @Router /authority/setDataAuthority [post] +export const setDataAuthority = (data) => { + return service({ + url: '/authority/setDataAuthority', + method: 'post', + data + }) +} + +// @Summary 修改角色 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysAuthority true "修改角色" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" +// @Router /authority/setDataAuthority [post] +export const updateAuthority = (data) => { + return service({ + url: '/authority/updateAuthority', + method: 'put', + data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/authorityBtn.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/authorityBtn.js new file mode 100644 index 000000000..9fe73bf42 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/authorityBtn.js @@ -0,0 +1,27 @@ + +import service from '@/utils/request' + +export const getAuthorityBtnApi = (data) => { + return service({ + url: '/authorityBtn/getAuthorityBtn', + method: 'post', + data + }) +} + +export const setAuthorityBtnApi = (data) => { + return service({ + url: '/authorityBtn/setAuthorityBtn', + method: 'post', + data + }) +} + +export const canRemoveAuthorityBtnApi = (params) => { + return service({ + url: '/authorityBtn/canRemoveAuthorityBtn', + method: 'post', + params + }) +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/autoCode.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/autoCode.js new file mode 100644 index 000000000..544330902 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/autoCode.js @@ -0,0 +1,189 @@ +import service from '@/utils/request' + +export const preview = (data) => { + return service({ + url: '/autoCode/preview', + method: 'post', + data + }) +} + +export const createTemp = (data) => { + return service({ + url: '/autoCode/createTemp', + method: 'post', + data + }) +} + +// @Tags SysApi +// @Summary 获取当前所有数据库 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /autoCode/getDatabase [get] +export const getDB = (params) => { + return service({ + url: '/autoCode/getDB', + method: 'get', + params + }) +} + +// @Tags SysApi +// @Summary 获取当前数据库所有表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /autoCode/getTables [get] +export const getTable = (params) => { + return service({ + url: '/autoCode/getTables', + method: 'get', + params + }) +} + +// @Tags SysApi +// @Summary 获取当前数据库所有表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /autoCode/getColumn [get] +export const getColumn = (params) => { + return service({ + url: '/autoCode/getColumn', + method: 'get', + params + }) +} + +export const getSysHistory = (data) => { + return service({ + url: '/autoCode/getSysHistory', + method: 'post', + data + }) +} + +export const rollback = (data) => { + return service({ + url: '/autoCode/rollback', + method: 'post', + data + }) +} + +export const getMeta = (data) => { + return service({ + url: '/autoCode/getMeta', + method: 'post', + data + }) +} + +export const delSysHistory = (data) => { + return service({ + url: '/autoCode/delSysHistory', + method: 'post', + data + }) +} + +export const createPackageApi = (data) => { + return service({ + url: '/autoCode/createPackage', + method: 'post', + data + }) +} + +export const getPackageApi = () => { + return service({ + url: '/autoCode/getPackage', + method: 'post' + }) +} + +export const deletePackageApi = (data) => { + return service({ + url: '/autoCode/delPackage', + method: 'post', + data + }) +} + +export const getTemplatesApi = () => { + return service({ + url: '/autoCode/getTemplates', + method: 'get' + }) +} + +export const installPlug = (data) => { + return service({ + url: '/autoCode/installPlug', + method: 'post', + data + }) +} + +export const pubPlug = (params) => { + return service({ + url: '/autoCode/pubPlug', + method: 'post', + params + }) +} + + +export const llmAuto = (data) => { + return service({ + url: '/autoCode/llmAuto', + method: 'post', + data:{...data,mode:'ai'}, + timeout: 1000 * 60 * 10, + loadingOption:{ + lock: true, + fullscreen:true, + text: `小淼正在思考,请稍候...`, + } + }) +} + + +export const butler = (data) => { + return service({ + url: '/autoCode/llmAuto', + method: 'post', + data:{...data,mode:'butler'}, + timeout: 1000 * 60 * 10, + }) +} + +export const addFunc = (data) => { + return service({ + url: '/autoCode/addFunc', + method: 'post', + data + }) +} + +export const initMenu = (data) => { + return service({ + url: '/autoCode/initMenu', + method: 'post', + data + }) +} + +export const initAPI = (data) => { + return service({ + url: '/autoCode/initAPI', + method: 'post', + data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/breakpoint.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/breakpoint.js new file mode 100644 index 000000000..1dbfba23e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/breakpoint.js @@ -0,0 +1,43 @@ +import service from '@/utils/request' +// @Summary 设置角色资源权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body sysModel.SysAuthority true "设置角色资源权限" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" +// @Router /authority/setDataAuthority [post] + +export const findFile = (params) => { + return service({ + url: '/fileUploadAndDownload/findFile', + method: 'get', + params + }) +} + +export const breakpointContinue = (data) => { + return service({ + url: '/fileUploadAndDownload/breakpointContinue', + method: 'post', + donNotShowLoading: true, + headers: { 'Content-Type': 'multipart/form-data' }, + data + }) +} + +export const breakpointContinueFinish = (params) => { + return service({ + url: '/fileUploadAndDownload/breakpointContinueFinish', + method: 'post', + params + }) +} + +export const removeChunk = (data, params) => { + return service({ + url: '/fileUploadAndDownload/removeChunk', + method: 'post', + data, + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/casbin.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/casbin.js new file mode 100644 index 000000000..802e13006 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/casbin.js @@ -0,0 +1,32 @@ +import service from '@/utils/request' +// @Tags authority +// @Summary 更改角色api权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateAuthorityPatams true "更改角色api权限" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /casbin/UpdateCasbin [post] +export const UpdateCasbin = (data) => { + return service({ + url: '/casbin/updateCasbin', + method: 'post', + data + }) +} + +// @Tags casbin +// @Summary 获取权限列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.CreateAuthorityPatams true "获取权限列表" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /casbin/getPolicyPathByAuthorityId [post] +export const getPolicyPathByAuthorityId = (data) => { + return service({ + url: '/casbin/getPolicyPathByAuthorityId', + method: 'post', + data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/customer.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/customer.js new file mode 100644 index 000000000..4776f1c09 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/customer.js @@ -0,0 +1,80 @@ +import service from '@/utils/request' +// @Tags SysApi +// @Summary 删除客户 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body dbModel.ExaCustomer true "删除客户" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /customer/customer [post] +export const createExaCustomer = (data) => { + return service({ + url: '/customer/customer', + method: 'post', + data + }) +} + +// @Tags SysApi +// @Summary 更新客户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body dbModel.ExaCustomer true "更新客户信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /customer/customer [put] +export const updateExaCustomer = (data) => { + return service({ + url: '/customer/customer', + method: 'put', + data + }) +} + +// @Tags SysApi +// @Summary 创建客户 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body dbModel.ExaCustomer true "创建客户" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /customer/customer [delete] +export const deleteExaCustomer = (data) => { + return service({ + url: '/customer/customer', + method: 'delete', + data + }) +} + +// @Tags SysApi +// @Summary 获取单一客户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body dbModel.ExaCustomer true "获取单一客户信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /customer/customer [get] +export const getExaCustomer = (params) => { + return service({ + url: '/customer/customer', + method: 'get', + params + }) +} + +// @Tags SysApi +// @Summary 获取权限客户列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body modelInterface.PageInfo true "获取权限客户列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /customer/customerList [get] +export const getExaCustomerList = (params) => { + return service({ + url: '/customer/customerList', + method: 'get', + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/email.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/email.js new file mode 100644 index 000000000..c2f16f430 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/email.js @@ -0,0 +1,14 @@ +import service from '@/utils/request' +// @Tags email +// @Summary 发送测试邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" +// @Router /email/emailTest [post] +export const emailTest = (data) => { + return service({ + url: '/email/emailTest', + method: 'post', + data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/exportTemplate.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/exportTemplate.js new file mode 100644 index 000000000..5b7b27827 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/exportTemplate.js @@ -0,0 +1,97 @@ +import service from '@/utils/request' + +// @Tags SysExportTemplate +// @Summary 创建导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysExportTemplate true "创建导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /sysExportTemplate/createSysExportTemplate [post] +export const createSysExportTemplate = (data) => { + return service({ + url: '/sysExportTemplate/createSysExportTemplate', + method: 'post', + data + }) +} + +// @Tags SysExportTemplate +// @Summary 删除导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysExportTemplate true "删除导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysExportTemplate/deleteSysExportTemplate [delete] +export const deleteSysExportTemplate = (data) => { + return service({ + url: '/sysExportTemplate/deleteSysExportTemplate', + method: 'delete', + data + }) +} + +// @Tags SysExportTemplate +// @Summary 批量删除导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysExportTemplate/deleteSysExportTemplate [delete] +export const deleteSysExportTemplateByIds = (data) => { + return service({ + url: '/sysExportTemplate/deleteSysExportTemplateByIds', + method: 'delete', + data + }) +} + +// @Tags SysExportTemplate +// @Summary 更新导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysExportTemplate true "更新导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /sysExportTemplate/updateSysExportTemplate [put] +export const updateSysExportTemplate = (data) => { + return service({ + url: '/sysExportTemplate/updateSysExportTemplate', + method: 'put', + data + }) +} + +// @Tags SysExportTemplate +// @Summary 用id查询导出模板 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.SysExportTemplate true "用id查询导出模板" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /sysExportTemplate/findSysExportTemplate [get] +export const findSysExportTemplate = (params) => { + return service({ + url: '/sysExportTemplate/findSysExportTemplate', + method: 'get', + params + }) +} + +// @Tags SysExportTemplate +// @Summary 分页获取导出模板列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取导出模板列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysExportTemplate/getSysExportTemplateList [get] +export const getSysExportTemplateList = (params) => { + return service({ + url: '/sysExportTemplate/getSysExportTemplateList', + method: 'get', + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/fileUploadAndDownload.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/fileUploadAndDownload.js new file mode 100644 index 000000000..2bff5bb47 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/fileUploadAndDownload.js @@ -0,0 +1,57 @@ +import service from '@/utils/request' +// @Tags FileUploadAndDownload +// @Summary 分页文件列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body modelInterface.PageInfo true "分页获取文件户列表" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /fileUploadAndDownload/getFileList [post] +export const getFileList = (data) => { + return service({ + url: '/fileUploadAndDownload/getFileList', + method: 'post', + data + }) +} + +// @Tags FileUploadAndDownload +// @Summary 删除文件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body dbModel.FileUploadAndDownload true "传入文件里面id即可" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"返回成功"}" +// @Router /fileUploadAndDownload/deleteFile [post] +export const deleteFile = (data) => { + return service({ + url: '/fileUploadAndDownload/deleteFile', + method: 'post', + data + }) +} + +/** + * 编辑文件名或者备注 + * @param data + * @returns {*} + */ +export const editFileName = (data) => { + return service({ + url: '/fileUploadAndDownload/editFileName', + method: 'post', + data + }) +} + +/** + * 导入URL + * @param data + * @returns {*} + */ +export const importURL = (data) => { + return service({ + url: '/fileUploadAndDownload/importURL', + method: 'post', + data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/github.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/github.js new file mode 100644 index 000000000..4dc4eed58 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/github.js @@ -0,0 +1,17 @@ +import axios from 'axios' + +const service = axios.create() + +export function Commits(page) { + return service({ + url: 'https://api.github.com/repos/flipped-aurora/gin-vue-admin/commits?page=' + page, + method: 'get' + }) +} + +export function Members() { + return service({ + url: 'https://api.github.com/orgs/FLIPPED-AURORA/members', + method: 'get' + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/abnormalTransactions.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/abnormalTransactions.js new file mode 100644 index 000000000..398f82404 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/abnormalTransactions.js @@ -0,0 +1,110 @@ +import service from '@/utils/request' +// @Tags AbnormalTransactions +// @Summary 创建异常交易 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.AbnormalTransactions true "创建异常交易" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /abnormalTransactions/createAbnormalTransactions [post] +export const createAbnormalTransactions = (data) => { + return service({ + url: '/abnormalTransactions/createAbnormalTransactions', + method: 'post', + data + }) +} + +// @Tags AbnormalTransactions +// @Summary 删除异常交易 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.AbnormalTransactions true "删除异常交易" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /abnormalTransactions/deleteAbnormalTransactions [delete] +export const deleteAbnormalTransactions = (params) => { + return service({ + url: '/abnormalTransactions/deleteAbnormalTransactions', + method: 'delete', + params + }) +} + +// @Tags AbnormalTransactions +// @Summary 批量删除异常交易 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除异常交易" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /abnormalTransactions/deleteAbnormalTransactions [delete] +export const deleteAbnormalTransactionsByIds = (params) => { + return service({ + url: '/abnormalTransactions/deleteAbnormalTransactionsByIds', + method: 'delete', + params + }) +} + +// @Tags AbnormalTransactions +// @Summary 更新异常交易 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.AbnormalTransactions true "更新异常交易" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /abnormalTransactions/updateAbnormalTransactions [put] +export const updateAbnormalTransactions = (data) => { + return service({ + url: '/abnormalTransactions/updateAbnormalTransactions', + method: 'put', + data + }) +} + +// @Tags AbnormalTransactions +// @Summary 用id查询异常交易 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.AbnormalTransactions true "用id查询异常交易" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /abnormalTransactions/findAbnormalTransactions [get] +export const findAbnormalTransactions = (params) => { + return service({ + url: '/abnormalTransactions/findAbnormalTransactions', + method: 'get', + params + }) +} + +// @Tags AbnormalTransactions +// @Summary 分页获取异常交易列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取异常交易列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /abnormalTransactions/getAbnormalTransactionsList [get] +export const getAbnormalTransactionsList = (params) => { + return service({ + url: '/abnormalTransactions/getAbnormalTransactionsList', + method: 'get', + params + }) +} + +// @Tags AbnormalTransactions +// @Summary 不需要鉴权的异常交易接口 +// @accept application/json +// @Produce application/json +// @Param data query hufuReq.AbnormalTransactionsSearch true "分页获取异常交易列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /abnormalTransactions/getAbnormalTransactionsPublic [get] +export const getAbnormalTransactionsPublic = () => { + return service({ + url: '/abnormalTransactions/getAbnormalTransactionsPublic', + method: 'get', + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/invoices.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/invoices.js new file mode 100644 index 000000000..8b0d17764 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/invoices.js @@ -0,0 +1,110 @@ +import service from '@/utils/request' +// @Tags Invoices +// @Summary 创建发票信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Invoices true "创建发票信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /invoices/createInvoices [post] +export const createInvoices = (data) => { + return service({ + url: '/invoices/createInvoices', + method: 'post', + data + }) +} + +// @Tags Invoices +// @Summary 删除发票信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Invoices true "删除发票信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /invoices/deleteInvoices [delete] +export const deleteInvoices = (params) => { + return service({ + url: '/invoices/deleteInvoices', + method: 'delete', + params + }) +} + +// @Tags Invoices +// @Summary 批量删除发票信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除发票信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /invoices/deleteInvoices [delete] +export const deleteInvoicesByIds = (params) => { + return service({ + url: '/invoices/deleteInvoicesByIds', + method: 'delete', + params + }) +} + +// @Tags Invoices +// @Summary 更新发票信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Invoices true "更新发票信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /invoices/updateInvoices [put] +export const updateInvoices = (data) => { + return service({ + url: '/invoices/updateInvoices', + method: 'put', + data + }) +} + +// @Tags Invoices +// @Summary 用id查询发票信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.Invoices true "用id查询发票信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /invoices/findInvoices [get] +export const findInvoices = (params) => { + return service({ + url: '/invoices/findInvoices', + method: 'get', + params + }) +} + +// @Tags Invoices +// @Summary 分页获取发票信息列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取发票信息列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /invoices/getInvoicesList [get] +export const getInvoicesList = (params) => { + return service({ + url: '/invoices/getInvoicesList', + method: 'get', + params + }) +} + +// @Tags Invoices +// @Summary 不需要鉴权的发票信息接口 +// @accept application/json +// @Produce application/json +// @Param data query hufuReq.InvoicesSearch true "分页获取发票信息列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /invoices/getInvoicesPublic [get] +export const getInvoicesPublic = () => { + return service({ + url: '/invoices/getInvoicesPublic', + method: 'get', + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/wallet.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/wallet.js new file mode 100644 index 000000000..9dff3592c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/hufu/wallet.js @@ -0,0 +1,110 @@ +import service from '@/utils/request' +// @Tags Wallet +// @Summary 创建钱包 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Wallet true "创建钱包" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /wallet/createWallet [post] +export const createWallet = (data) => { + return service({ + url: '/wallet/createWallet', + method: 'post', + data + }) +} + +// @Tags Wallet +// @Summary 删除钱包 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Wallet true "删除钱包" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /wallet/deleteWallet [delete] +export const deleteWallet = (params) => { + return service({ + url: '/wallet/deleteWallet', + method: 'delete', + params + }) +} + +// @Tags Wallet +// @Summary 批量删除钱包 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除钱包" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /wallet/deleteWallet [delete] +export const deleteWalletByIds = (params) => { + return service({ + url: '/wallet/deleteWalletByIds', + method: 'delete', + params + }) +} + +// @Tags Wallet +// @Summary 更新钱包 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Wallet true "更新钱包" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /wallet/updateWallet [put] +export const updateWallet = (data) => { + return service({ + url: '/wallet/updateWallet', + method: 'put', + data + }) +} + +// @Tags Wallet +// @Summary 用id查询钱包 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.Wallet true "用id查询钱包" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /wallet/findWallet [get] +export const findWallet = (params) => { + return service({ + url: '/wallet/findWallet', + method: 'get', + params + }) +} + +// @Tags Wallet +// @Summary 分页获取钱包列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取钱包列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /wallet/getWalletList [get] +export const getWalletList = (params) => { + return service({ + url: '/wallet/getWalletList', + method: 'get', + params + }) +} + +// @Tags Wallet +// @Summary 不需要鉴权的钱包接口 +// @accept application/json +// @Produce application/json +// @Param data query hufuReq.WalletSearch true "分页获取钱包列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /wallet/getWalletPublic [get] +export const getWalletPublic = () => { + return service({ + url: '/wallet/getWalletPublic', + method: 'get', + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/initdb.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/initdb.js new file mode 100644 index 000000000..f1eb2f4ad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/initdb.js @@ -0,0 +1,27 @@ +import service from '@/utils/request' +// @Tags InitDB +// @Summary 初始化用户数据库 +// @Produce application/json +// @Param data body request.InitDB true "初始化数据库参数" +// @Success 200 {string} string "{"code":0,"data":{},"msg":"自动创建数据库成功"}" +// @Router /init/initdb [post] +export const initDB = (data) => { + return service({ + url: '/init/initdb', + method: 'post', + data, + donNotShowLoading: true + }) +} + +// @Tags CheckDB +// @Summary 初始化用户数据库 +// @Produce application/json +// @Success 200 {string} string "{"code":0,"data":{},"msg":"探测完成"}" +// @Router /init/checkdb [post] +export const checkDB = () => { + return service({ + url: '/init/checkdb', + method: 'post' + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/jwt.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/jwt.js new file mode 100644 index 000000000..811ffc4f4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/jwt.js @@ -0,0 +1,14 @@ +import service from '@/utils/request' +// @Tags jwt +// @Summary jwt加入黑名单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}" +// @Router /jwt/jsonInBlacklist [post] +export const jsonInBlacklist = () => { + return service({ + url: '/jwt/jsonInBlacklist', + method: 'post' + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/menu.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/menu.js new file mode 100644 index 000000000..163b5a697 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/menu.js @@ -0,0 +1,113 @@ +import service from '@/utils/request' +// @Summary 用户登录 获取动态路由 +// @Produce application/json +// @Param 可以什么都不填 调一下即可 +// @Router /menu/getMenu [post] +export const asyncMenu = () => { + return service({ + url: '/menu/getMenu', + method: 'post' + }) +} + +// @Summary 获取menu列表 +// @Produce application/json +// @Param { +// page int +// pageSize int +// } +// @Router /menu/getMenuList [post] +export const getMenuList = (data) => { + return service({ + url: '/menu/getMenuList', + method: 'post', + data + }) +} + +// @Summary 新增基础menu +// @Produce application/json +// @Param menu Object +// @Router /menu/getMenuList [post] +export const addBaseMenu = (data) => { + return service({ + url: '/menu/addBaseMenu', + method: 'post', + data + }) +} + +// @Summary 获取基础路由列表 +// @Produce application/json +// @Param 可以什么都不填 调一下即可 +// @Router /menu/getBaseMenuTree [post] +export const getBaseMenuTree = () => { + return service({ + url: '/menu/getBaseMenuTree', + method: 'post' + }) +} + +// @Summary 添加用户menu关联关系 +// @Produce application/json +// @Param menus Object authorityId string +// @Router /menu/getMenuList [post] +export const addMenuAuthority = (data) => { + return service({ + url: '/menu/addMenuAuthority', + method: 'post', + data + }) +} + +// @Summary 获取用户menu关联关系 +// @Produce application/json +// @Param authorityId string +// @Router /menu/getMenuAuthority [post] +export const getMenuAuthority = (data) => { + return service({ + url: '/menu/getMenuAuthority', + method: 'post', + data + }) +} + +// @Summary 删除menu +// @Produce application/json +// @Param ID float64 +// @Router /menu/deleteBaseMenu [post] +export const deleteBaseMenu = (data) => { + return service({ + url: '/menu/deleteBaseMenu', + method: 'post', + data + }) +} + +// @Summary 修改menu列表 +// @Produce application/json +// @Param menu Object +// @Router /menu/updateBaseMenu [post] +export const updateBaseMenu = (data) => { + return service({ + url: '/menu/updateBaseMenu', + method: 'post', + data + }) +} + +// @Tags menu +// @Summary 根据id获取菜单 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.GetById true "根据id获取菜单" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /menu/getBaseMenuById [post] +export const getBaseMenuById = (data) => { + return service({ + url: '/menu/getBaseMenuById', + method: 'post', + data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysDictionary.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysDictionary.js new file mode 100644 index 000000000..f5d6c8620 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysDictionary.js @@ -0,0 +1,80 @@ +import service from '@/utils/request' +// @Tags SysDictionary +// @Summary 创建SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionary true "创建SysDictionary" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysDictionary/createSysDictionary [post] +export const createSysDictionary = (data) => { + return service({ + url: '/sysDictionary/createSysDictionary', + method: 'post', + data + }) +} + +// @Tags SysDictionary +// @Summary 删除SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionary true "删除SysDictionary" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysDictionary/deleteSysDictionary [delete] +export const deleteSysDictionary = (data) => { + return service({ + url: '/sysDictionary/deleteSysDictionary', + method: 'delete', + data + }) +} + +// @Tags SysDictionary +// @Summary 更新SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionary true "更新SysDictionary" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /sysDictionary/updateSysDictionary [put] +export const updateSysDictionary = (data) => { + return service({ + url: '/sysDictionary/updateSysDictionary', + method: 'put', + data + }) +} + +// @Tags SysDictionary +// @Summary 用id查询SysDictionary +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionary true "用id查询SysDictionary" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /sysDictionary/findSysDictionary [get] +export const findSysDictionary = (params) => { + return service({ + url: '/sysDictionary/findSysDictionary', + method: 'get', + params + }) +} + +// @Tags SysDictionary +// @Summary 分页获取SysDictionary列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "分页获取SysDictionary列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysDictionary/getSysDictionaryList [get] +export const getSysDictionaryList = (params) => { + return service({ + url: '/sysDictionary/getSysDictionaryList', + method: 'get', + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysDictionaryDetail.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysDictionaryDetail.js new file mode 100644 index 000000000..d4f877224 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysDictionaryDetail.js @@ -0,0 +1,80 @@ +import service from '@/utils/request' +// @Tags SysDictionaryDetail +// @Summary 创建SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionaryDetail true "创建SysDictionaryDetail" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysDictionaryDetail/createSysDictionaryDetail [post] +export const createSysDictionaryDetail = (data) => { + return service({ + url: '/sysDictionaryDetail/createSysDictionaryDetail', + method: 'post', + data + }) +} + +// @Tags SysDictionaryDetail +// @Summary 删除SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionaryDetail true "删除SysDictionaryDetail" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete] +export const deleteSysDictionaryDetail = (data) => { + return service({ + url: '/sysDictionaryDetail/deleteSysDictionaryDetail', + method: 'delete', + data + }) +} + +// @Tags SysDictionaryDetail +// @Summary 更新SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionaryDetail true "更新SysDictionaryDetail" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put] +export const updateSysDictionaryDetail = (data) => { + return service({ + url: '/sysDictionaryDetail/updateSysDictionaryDetail', + method: 'put', + data + }) +} + +// @Tags SysDictionaryDetail +// @Summary 用id查询SysDictionaryDetail +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysDictionaryDetail true "用id查询SysDictionaryDetail" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /sysDictionaryDetail/findSysDictionaryDetail [get] +export const findSysDictionaryDetail = (params) => { + return service({ + url: '/sysDictionaryDetail/findSysDictionaryDetail', + method: 'get', + params + }) +} + +// @Tags SysDictionaryDetail +// @Summary 分页获取SysDictionaryDetail列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "分页获取SysDictionaryDetail列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get] +export const getSysDictionaryDetailList = (params) => { + return service({ + url: '/sysDictionaryDetail/getSysDictionaryDetailList', + method: 'get', + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysOperationRecord.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysOperationRecord.js new file mode 100644 index 000000000..4428c036f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysOperationRecord.js @@ -0,0 +1,48 @@ +import service from '@/utils/request' +// @Tags SysOperationRecord +// @Summary 删除SysOperationRecord +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysOperationRecord true "删除SysOperationRecord" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysOperationRecord/deleteSysOperationRecord [delete] +export const deleteSysOperationRecord = (data) => { + return service({ + url: '/sysOperationRecord/deleteSysOperationRecord', + method: 'delete', + data + }) +} + +// @Tags SysOperationRecord +// @Summary 删除SysOperationRecord +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "删除SysOperationRecord" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysOperationRecord/deleteSysOperationRecord [delete] +export const deleteSysOperationRecordByIds = (data) => { + return service({ + url: '/sysOperationRecord/deleteSysOperationRecordByIds', + method: 'delete', + data + }) +} + +// @Tags SysOperationRecord +// @Summary 分页获取SysOperationRecord列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.PageInfo true "分页获取SysOperationRecord列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysOperationRecord/getSysOperationRecordList [get] +export const getSysOperationRecordList = (params) => { + return service({ + url: '/sysOperationRecord/getSysOperationRecordList', + method: 'get', + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysParams.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysParams.js new file mode 100644 index 000000000..348f1b5e4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/sysParams.js @@ -0,0 +1,111 @@ +import service from '@/utils/request' +// @Tags SysParams +// @Summary 创建参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysParams true "创建参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /sysParams/createSysParams [post] +export const createSysParams = (data) => { + return service({ + url: '/sysParams/createSysParams', + method: 'post', + data + }) +} + +// @Tags SysParams +// @Summary 删除参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysParams true "删除参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysParams/deleteSysParams [delete] +export const deleteSysParams = (params) => { + return service({ + url: '/sysParams/deleteSysParams', + method: 'delete', + params + }) +} + +// @Tags SysParams +// @Summary 批量删除参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /sysParams/deleteSysParams [delete] +export const deleteSysParamsByIds = (params) => { + return service({ + url: '/sysParams/deleteSysParamsByIds', + method: 'delete', + params + }) +} + +// @Tags SysParams +// @Summary 更新参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysParams true "更新参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /sysParams/updateSysParams [put] +export const updateSysParams = (data) => { + return service({ + url: '/sysParams/updateSysParams', + method: 'put', + data + }) +} + +// @Tags SysParams +// @Summary 用id查询参数 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.SysParams true "用id查询参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /sysParams/findSysParams [get] +export const findSysParams = (params) => { + return service({ + url: '/sysParams/findSysParams', + method: 'get', + params + }) +} + +// @Tags SysParams +// @Summary 分页获取参数列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取参数列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /sysParams/getSysParamsList [get] +export const getSysParamsList = (params) => { + return service({ + url: '/sysParams/getSysParamsList', + method: 'get', + params + }) +} + +// @Tags SysParams +// @Summary 不需要鉴权的参数接口 +// @accept application/json +// @Produce application/json +// @Param data query systemReq.SysParamsSearch true "分页获取参数列表" +// @Success 200 {object} response.Response{data=object,msg=string} "获取成功" +// @Router /sysParams/getSysParam [get] +export const getSysParam = (params) => { + return service({ + url: '/sysParams/getSysParam', + method: 'get', + params + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/system.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/system.js new file mode 100644 index 000000000..4dd5eca25 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/system.js @@ -0,0 +1,56 @@ +import service from '@/utils/request' +// @Tags systrm +// @Summary 获取配置文件内容 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" +// @Router /system/getSystemConfig [post] +export const getSystemConfig = () => { + return service({ + url: '/system/getSystemConfig', + method: 'post' + }) +} + +// @Tags system +// @Summary 设置配置文件内容 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body sysModel.System true +// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" +// @Router /system/setSystemConfig [post] +export const setSystemConfig = (data) => { + return service({ + url: '/system/setSystemConfig', + method: 'post', + data + }) +} + +// @Tags system +// @Summary 获取服务器运行状态 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" +// @Router /system/getServerInfo [post] +export const getSystemState = () => { + return service({ + url: '/system/getServerInfo', + method: 'post', + donNotShowLoading: true + }) +} + +/** + * 重启服务 + * @param data + * @returns {*} + */ +export const reloadSystem = (data) => { + return service({ + url: '/system/reloadSystem', + method: 'post', + data + }) +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/user.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/user.js new file mode 100644 index 000000000..2b357d031 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/api/user.js @@ -0,0 +1,181 @@ +import service from '@/utils/request' +// @Summary 用户登录 +// @Produce application/json +// @Param data body {username:"string",password:"string"} +// @Router /base/login [post] +export const login = (data) => { + return service({ + url: '/base/login', + method: 'post', + data: data + }) +} + +// @Summary 获取验证码 +// @Produce application/json +// @Param data body {username:"string",password:"string"} +// @Router /base/captcha [post] +export const captcha = () => { + return service({ + url: '/base/captcha', + method: 'post' + }) +} + +// @Summary 用户注册 +// @Produce application/json +// @Param data body {username:"string",password:"string"} +// @Router /base/resige [post] +export const register = (data) => { + return service({ + url: '/user/admin_register', + method: 'post', + data: data + }) +} + +// @Summary 修改密码 +// @Produce application/json +// @Param data body {username:"string",password:"string",newPassword:"string"} +// @Router /user/changePassword [post] +export const changePassword = (data) => { + return service({ + url: '/user/changePassword', + method: 'post', + data: data + }) +} + +// @Tags User +// @Summary 分页获取用户列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body modelInterface.PageInfo true "分页获取用户列表" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /user/getUserList [post] +export const getUserList = (data) => { + return service({ + url: '/user/getUserList', + method: 'post', + data: data + }) +} + +// @Tags User +// @Summary 设置用户权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.SetUserAuth true "设置用户权限" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"修改成功"}" +// @Router /user/setUserAuthority [post] +export const setUserAuthority = (data) => { + return service({ + url: '/user/setUserAuthority', + method: 'post', + data: data + }) +} + +// @Tags SysUser +// @Summary 删除用户 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.SetUserAuth true "删除用户" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}" +// @Router /user/deleteUser [delete] +export const deleteUser = (data) => { + return service({ + url: '/user/deleteUser', + method: 'delete', + data: data + }) +} + +// @Tags SysUser +// @Summary 设置用户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysUser true "设置用户信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}" +// @Router /user/setUserInfo [put] +export const setUserInfo = (data) => { + return service({ + url: '/user/setUserInfo', + method: 'put', + data: data + }) +} + +// @Tags SysUser +// @Summary 设置用户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysUser true "设置用户信息" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}" +// @Router /user/setSelfInfo [put] +export const setSelfInfo = (data) => { + return service({ + url: '/user/setSelfInfo', + method: 'put', + data: data + }) +} + +// @Tags SysUser +// @Summary 设置自身界面配置 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.SysUser true "设置自身界面配置" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}" +// @Router /user/setSelfSetting [put] +export const setSelfSetting = (data) => { + return service({ + url: '/user/setSelfSetting', + method: 'put', + data: data + }) +} + +// @Tags User +// @Summary 设置用户权限 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body api.setUserAuthorities true "设置用户权限" +// @Success 200 {string} json "{"success":true,"data":{},"msg":"修改成功"}" +// @Router /user/setUserAuthorities [post] +export const setUserAuthorities = (data) => { + return service({ + url: '/user/setUserAuthorities', + method: 'post', + data: data + }) +} + +// @Tags User +// @Summary 获取用户信息 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /user/getUserInfo [get] +export const getUserInfo = () => { + return service({ + url: '/user/getUserInfo', + method: 'get' + }) +} + +export const resetPassword = (data) => { + return service({ + url: '/user/resetPassword', + method: 'post', + data: data + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/arrayCtrl/arrayCtrl.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/arrayCtrl/arrayCtrl.vue new file mode 100644 index 000000000..08b4ce219 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/arrayCtrl/arrayCtrl.vue @@ -0,0 +1,68 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/bottomInfo/bottomInfo.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/bottomInfo/bottomInfo.vue new file mode 100644 index 000000000..6e98955d6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/bottomInfo/bottomInfo.vue @@ -0,0 +1,27 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/charts/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/charts/index.vue new file mode 100644 index 000000000..4ea5a75d1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/charts/index.vue @@ -0,0 +1,56 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/commandMenu/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/commandMenu/index.vue new file mode 100644 index 000000000..a36d174d2 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/commandMenu/index.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/customPic/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/customPic/index.vue new file mode 100644 index 000000000..63743d535 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/customPic/index.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/exportExcel.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/exportExcel.vue new file mode 100644 index 000000000..26ceb1875 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/exportExcel.vue @@ -0,0 +1,58 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/exportTemplate.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/exportTemplate.vue new file mode 100644 index 000000000..9e099ebae --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/exportTemplate.vue @@ -0,0 +1,28 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/importExcel.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/importExcel.vue new file mode 100644 index 000000000..2f6f28d30 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/exportExcel/importExcel.vue @@ -0,0 +1,42 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/docx.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/docx.vue new file mode 100644 index 000000000..35e3e5a5a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/docx.vue @@ -0,0 +1,35 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/excel.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/excel.vue new file mode 100644 index 000000000..ac449dac6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/excel.vue @@ -0,0 +1,33 @@ + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/index.vue new file mode 100644 index 000000000..a6704d6ba --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/index.vue @@ -0,0 +1,53 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/pdf.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/pdf.vue new file mode 100644 index 000000000..8098f06ff --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/office/pdf.vue @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/richtext/rich-edit.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/richtext/rich-edit.vue new file mode 100644 index 000000000..cee3b2ec1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/richtext/rich-edit.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/richtext/rich-view.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/richtext/rich-view.vue new file mode 100644 index 000000000..fd6616e4a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/richtext/rich-view.vue @@ -0,0 +1,62 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectFile/selectFile.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectFile/selectFile.vue new file mode 100644 index 000000000..81c03c56f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectFile/selectFile.vue @@ -0,0 +1,87 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectImage/selectComponent.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectImage/selectComponent.vue new file mode 100644 index 000000000..1ff6e04cf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectImage/selectComponent.vue @@ -0,0 +1,65 @@ + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectImage/selectImage.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectImage/selectImage.vue new file mode 100644 index 000000000..0ca5ce72e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/selectImage/selectImage.vue @@ -0,0 +1,276 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/svgIcon/svgIcon.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/svgIcon/svgIcon.vue new file mode 100644 index 000000000..c6f766532 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/svgIcon/svgIcon.vue @@ -0,0 +1,39 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/upload/common.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/upload/common.vue new file mode 100644 index 000000000..97255da11 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/upload/common.vue @@ -0,0 +1,76 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/upload/image.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/upload/image.vue new file mode 100644 index 000000000..e65bcf74d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/upload/image.vue @@ -0,0 +1,92 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/warningBar/warningBar.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/warningBar/warningBar.vue new file mode 100644 index 000000000..4fac6c0b9 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/components/warningBar/warningBar.vue @@ -0,0 +1,33 @@ + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/config.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/config.js new file mode 100644 index 000000000..3e5937f37 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/config.js @@ -0,0 +1,20 @@ +/** + * 网站配置文件 + */ +const greenText = (text) => `\x1b[32m${text}\x1b[0m`; + +const config = { + appName: '数字虎符', + appLogo: 'logo.png', + showViteLogo: true, + logs: [], +} + +export const viteLogo = (env) => { + if (config.showViteLogo) { + + console.log('\n'); + } +} + +export default config diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/gin-vue-admin.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/gin-vue-admin.js new file mode 100644 index 000000000..b2ec789b7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/gin-vue-admin.js @@ -0,0 +1,16 @@ +/* + * gin-vue-admin web框架组 + * + * */ +// 加载网站配置文件夹 +import { register } from './global' + +export default { + install: (app) => { + register(app) + console.log(` + 默认自动化文档地址:http://127.0.0.1:${import.meta.env.VITE_SERVER_PORT}/swagger/index.html + 默认前端文件运行地址:http://127.0.0.1:${import.meta.env.VITE_CLI_PORT} + `) + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/global.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/global.js new file mode 100644 index 000000000..2ef1ffef4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/core/global.js @@ -0,0 +1,53 @@ +import config from './config' +import { h } from 'vue' + +// 统一导入el-icon图标 +import * as ElIconModules from '@element-plus/icons-vue' +import svgIcon from '@/components/svgIcon/svgIcon.vue' +// 导入转换图标名称的函数 + +const createIconComponent = (name) => ({ + name: 'SvgIcon', + render() { + return h(svgIcon, { + name: name, + }) + }, +}) + +const registerIcons = async(app) => { + const iconModules = import.meta.glob('@/assets/icons/**/*.svg') // 系统目录 svg 图标 + const pluginIconModules = import.meta.glob('@/plugin/**/assets/icons/**/*.svg') // 插件目录 svg 图标 + const mergedIconModules = Object.assign({}, iconModules, pluginIconModules); // 合并所有 svg 图标 + for (const path in mergedIconModules) { + let pluginName = "" + if (path.startsWith("/src/plugin/")) { + pluginName = `${path.split('/')[3]}-` + } + const iconName = path.split('/').pop().replace(/\.svg$/, '') + // 如果iconName带空格则不加入到图标库中并且提示名称不合法 + if (iconName.indexOf(' ') !== -1) { + console.error(`icon ${iconName}.svg includes whitespace in ${path}`) + continue + } + const key = `${pluginName}${iconName}` + // 开发模式下列出所有 svg 图标,方便开发者直接查找复制使用 + import.meta.env.MODE == 'development' && console.log(`svg-icon-component: <${key} />`) + const iconComponent = createIconComponent(key) + config.logs.push({ + 'key': key, + 'label': key, + }) + app.component(key, iconComponent) + } +} + +export const register = (app) => { + // 统一注册el-icon图标 + for (const iconName in ElIconModules) { + app.component(iconName, ElIconModules[iconName]) + } + app.component('SvgIcon', svgIcon) + registerIcons(app) + app.config.globalProperties.$GIN_VUE_ADMIN = config +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/directive/auth.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/directive/auth.js new file mode 100644 index 000000000..50594d45f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/directive/auth.js @@ -0,0 +1,41 @@ +// 权限按钮展示指令 +import { useUserStore } from '@/pinia/modules/user' +export default { + install: (app) => { + const userStore = useUserStore() + app.directive('auth', { + // 当被绑定的元素插入到 DOM 中时…… + mounted: function(el, binding) { + const userInfo = userStore.userInfo + let type = '' + switch (Object.prototype.toString.call(binding.value)) { + case '[object Array]': + type = 'Array' + break + case '[object String]': + type = 'String' + break + case '[object Number]': + type = 'Number' + break + default: + type = '' + break + } + if (type === '') { + el.parentNode.removeChild(el) + return + } + const waitUse = binding.value.toString().split(',') + let flag = waitUse.some(item => Number(item) === userInfo.authorityId) + if (binding.modifiers.not) { + flag = !flag + } + if (!flag) { + el.parentNode.removeChild(el) + } + } + }) + } +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/charts.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/charts.js new file mode 100644 index 000000000..e7a14b1b0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/charts.js @@ -0,0 +1,19 @@ +// 本组件参考 arco-pro 的实现 +// https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/hooks/chart-option.ts + +import { computed } from 'vue'; +import { useAppStore } from '@/pinia'; + + +export default function useChartOption(sourceOption) { + const appStore = useAppStore(); + const isDark = computed(() => { + return appStore.theme === 'dark'; + }); + const chartOption = computed(() => { + return sourceOption(isDark.value); + }); + return { + chartOption, + }; +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/responsive.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/responsive.js new file mode 100644 index 000000000..417c1f999 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/responsive.js @@ -0,0 +1,35 @@ +// 本组件参考 arco-pro 的实现 +// https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/hooks/responsive.ts + +import { onMounted, onBeforeMount, onBeforeUnmount } from 'vue'; +import { useDebounceFn } from '@vueuse/core'; +import { useAppStore } from '@/pinia'; +import { addEventListen, removeEventListen } from '@/utils/event'; + +const WIDTH = 992; + +function queryDevice() { + const rect = document.body.getBoundingClientRect(); + return rect.width - 1 < WIDTH; +} + +export default function useResponsive(immediate) { + const appStore = useAppStore(); + function resizeHandler() { + if (!document.hidden) { + const isMobile = queryDevice(); + appStore.toggleDevice(isMobile ? 'mobile' : 'desktop'); + // appStore.toggleDevice(isMobile); + } + } + const debounceFn = useDebounceFn(resizeHandler, 100); + onMounted(() => { + if (immediate) debounceFn(); + }); + onBeforeMount(() => { + addEventListen(window, 'resize', debounceFn); + }); + onBeforeUnmount(() => { + removeEventListen(window, 'resize', debounceFn); + }); +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/use-windows-resize.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/use-windows-resize.js new file mode 100644 index 000000000..4ddf3f0fb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/hooks/use-windows-resize.js @@ -0,0 +1,23 @@ +// 监听 window 的 resize 事件,返回当前窗口的宽高 +import { shallowRef } from 'vue' +import { tryOnMounted, useEventListener } from '@vueuse/core' + +const width = shallowRef(0) +const height = shallowRef(0) + +export const useWindowResize = (cb) => { + const onResize = () => { + width.value = window.innerWidth + height.value = window.innerHeight + if (cb && typeof cb === 'function') { + cb(width.value, height.value) + } + } + + tryOnMounted(onResize) + useEventListener('resize', onResize, { passive: true }) + return { + width, + height, + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/main.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/main.js new file mode 100644 index 000000000..766c970d1 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/main.js @@ -0,0 +1,29 @@ +import './style/element_visiable.scss' +import 'element-plus/theme-chalk/dark/css-vars.css' +import { createApp } from 'vue' +import ElementPlus from 'element-plus' + +import 'element-plus/dist/index.css' +// 引入gin-vue-admin前端初始化相关内容 +import './core/gin-vue-admin' +// 引入封装的router +import router from '@/router/index' +import '@/permission' +import run from '@/core/gin-vue-admin.js' +import auth from '@/directive/auth' +import { store } from '@/pinia' +import App from './App.vue' +//消除警告 +import 'default-passive-events' + +const app = createApp(App) +app.config.productionTip = false + +app + .use(run) + .use(ElementPlus) + .use(store) + .use(auth) + .use(router) + .mount('#app') +export default app diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pathInfo.json b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pathInfo.json new file mode 100644 index 000000000..b31d395ed --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pathInfo.json @@ -0,0 +1,54 @@ +{ + "/src/view/about/index.vue": "About", + "/src/view/dashboard/index.vue": "Dashboard", + "/src/view/error/index.vue": "Error", + "/src/view/error/reload.vue": "Reload", + "/src/view/example/breakpoint/breakpoint.vue": "BreakPoint", + "/src/view/example/customer/customer.vue": "Customer", + "/src/view/example/index.vue": "Example", + "/src/view/example/upload/upload.vue": "Upload", + "/src/view/hufu/abnormalTransactions/abnormalTransactions.vue": "AbnormalTransactions", + "/src/view/hufu/abnormalTransactions/abnormalTransactionsForm.vue": "AbnormalTransactionsForm", + "/src/view/hufu/invoices/invoices.vue": "Invoices", + "/src/view/hufu/invoices/invoicesForm.vue": "InvoicesForm", + "/src/view/init/index.vue": "Init", + "/src/view/layout/aside/asideComponent/asyncSubmenu.vue": "AsyncSubmenu", + "/src/view/layout/aside/asideComponent/index.vue": "AsideComponent", + "/src/view/layout/aside/asideComponent/menuItem.vue": "MenuItem", + "/src/view/layout/aside/combinationMode.vue": "GvaAside", + "/src/view/layout/aside/headMode.vue": "GvaAside", + "/src/view/layout/aside/normalMode.vue": "GvaAside", + "/src/view/layout/index.vue": "GvaLayout", + "/src/view/layout/screenfull/index.vue": "Screenfull", + "/src/view/layout/search/search.vue": "BtnBox", + "/src/view/layout/setting/index.vue": "GvaSetting", + "/src/view/layout/tabs/index.vue": "HistoryComponent", + "/src/view/login/index.vue": "Login", + "/src/view/person/person.vue": "Person", + "/src/view/routerHolder.vue": "RouterHolder", + "/src/view/superAdmin/api/api.vue": "Api", + "/src/view/superAdmin/authority/authority.vue": "Authority", + "/src/view/superAdmin/authority/components/apis.vue": "Apis", + "/src/view/superAdmin/authority/components/datas.vue": "Datas", + "/src/view/superAdmin/authority/components/menus.vue": "Menus", + "/src/view/superAdmin/dictionary/sysDictionary.vue": "SysDictionary", + "/src/view/superAdmin/dictionary/sysDictionaryDetail.vue": "SysDictionaryDetail", + "/src/view/superAdmin/index.vue": "SuperAdmin", + "/src/view/superAdmin/menu/icon.vue": "Icon", + "/src/view/superAdmin/menu/menu.vue": "Menus", + "/src/view/superAdmin/operation/sysOperationRecord.vue": "SysOperationRecord", + "/src/view/superAdmin/params/sysParams.vue": "SysParams", + "/src/view/superAdmin/user/user.vue": "User", + "/src/view/system/state.vue": "State", + "/src/view/systemTools/autoCode/component/fieldDialog.vue": "FieldDialog", + "/src/view/systemTools/autoCode/index.vue": "AutoCode", + "/src/view/systemTools/autoCodeAdmin/index.vue": "AutoCodeAdmin", + "/src/view/systemTools/autoPkg/autoPkg.vue": "AutoPkg", + "/src/view/systemTools/exportTemplate/exportTemplate.vue": "ExportTemplate", + "/src/view/systemTools/formCreate/index.vue": "FormGenerator", + "/src/view/systemTools/index.vue": "System", + "/src/view/systemTools/system/system.vue": "Config", + "/src/plugin/announcement/form/info.vue": "InfoForm", + "/src/plugin/announcement/view/info.vue": "Info", + "/src/plugin/email/view/index.vue": "Email" +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/permission.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/permission.js new file mode 100644 index 000000000..b0c39af51 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/permission.js @@ -0,0 +1,139 @@ +import { useUserStore } from '@/pinia/modules/user' +import { useRouterStore } from '@/pinia/modules/router' +import getPageTitle from '@/utils/page' +import router from '@/router' +import Nprogress from 'nprogress' +import 'nprogress/nprogress.css' +Nprogress.configure({ showSpinner: false, ease: 'ease', speed: 500 }) + +const whiteList = ['Login', 'Init'] + +const getRouter = async(userStore) => { + const routerStore = useRouterStore() + await routerStore.SetAsyncRouter() + await userStore.GetUserInfo() + const asyncRouters = routerStore.asyncRouters + asyncRouters.forEach(asyncRouter => { + router.addRoute(asyncRouter) + }) +} + +const removeLoading = () => { + const element = document.getElementById('gva-loading-box'); + if (element) { + element.remove(); + } +} + + +async function handleKeepAlive(to) { + if (to.matched.some(item => item.meta.keepAlive)) { + if (to.matched && to.matched.length > 2) { + for (let i = 1; i < to.matched.length; i++) { + const element = to.matched[i - 1] + if (element.name === 'layout') { + to.matched.splice(i, 1) + await handleKeepAlive(to) + } + // 如果没有按需加载完成则等待加载 + if (typeof element.components.default === 'function') { + await element.components.default() + await handleKeepAlive(to) + } + } + } + } +} + +router.beforeEach(async(to, from) => { + const routerStore = useRouterStore() + Nprogress.start() + const userStore = useUserStore() + to.meta.matched = [...to.matched] + handleKeepAlive(to) + const token = userStore.token + // 在白名单中的判断情况 + document.title = getPageTitle(to.meta.title, to) + if(to.meta.client) { + return true + } + if (whiteList.indexOf(to.name) > -1) { + if (token) { + if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) { + await getRouter(userStore) + } + // token 可以解析但是却是不存在的用户 id 或角色 id 会导致无限调用 + if (userStore.userInfo?.authority?.defaultRouter != null) { + if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) { + return { name: userStore.userInfo.authority.defaultRouter } + } else { + return { path: '/layout/404' } + } + } else { + // 强制退出账号 + userStore.ClearStorage() + return { + name: 'Login', + query: { + redirect: document.location.hash + } + } + } + } else { + return true + } + } else { + // 不在白名单中并且已经登录的时候 + if (token) { + if(sessionStorage.getItem("needToHome") === 'true') { + sessionStorage.removeItem("needToHome") + return { path: '/'} + } + // 添加flag防止多次获取动态路由和栈溢出 + if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) { + await getRouter(userStore) + if (userStore.token) { + if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) { + return { ...to, replace: true } + } else { + return { path: '/layout/404' } + } + } else { + return { + name: 'Login', + query: { redirect: to.href } + } + } + } else { + if (to.matched.length) { + return true + } else { + return { path: '/layout/404' } + } + } + } + // 不在白名单中并且未登录的时候 + if (!token) { + return { + name: 'Login', + query: { + redirect: document.location.hash + } + } + } + } +}) + + +router.afterEach(() => { + // 路由加载完成后关闭进度条 + document.getElementsByClassName('main-cont main-right')[0]?.scrollTo(0, 0) + Nprogress.done() +}) + +router.onError(() => { + // 路由发生错误后销毁进度条 + Nprogress.remove() +}) + +removeLoading() diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/index.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/index.js new file mode 100644 index 000000000..49c389e99 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/index.js @@ -0,0 +1,13 @@ +import { createPinia } from 'pinia' +import { useAppStore } from '@/pinia/modules/app' +import { useUserStore } from '@/pinia/modules/user' +import { useDictionaryStore } from '@/pinia/modules/dictionary' + +const store = createPinia() + +export { + store, + useAppStore, + useUserStore, + useDictionaryStore +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/app.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/app.js new file mode 100644 index 000000000..44707cc37 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/app.js @@ -0,0 +1,146 @@ +import { defineStore } from 'pinia' +import { ref, watchEffect, reactive } from 'vue' +import { setBodyPrimaryColor } from '@/utils/format' +export const useAppStore = defineStore('app', () => { + + const device = ref("") + const config = reactive({ + weakness: false, + grey: false, + primaryColor: '#3b82f6', + showTabs: true, + darkMode: 'auto', + layout_side_width : 256, + layout_side_collapsed_width : 80, + layout_side_item_height : 48, + show_watermark: false, + side_mode : 'normal' + }) + + const theme = ref( 'auto') + + const toggleTheme = (dark) => { + if (dark) { + theme.value = 'dark'; + } else { + theme.value = 'light'; + } + } + + const toggleWeakness = (e) => { + config.weakness = e; + } + + const toggleGrey = (e) => { + config.grey = e; + } + + const togglePrimaryColor = (e) => { + config.primaryColor = e; + } + + const toggleTabs = (e) => { + config.showTabs = e; + } + + const toggleDevice = (e) => { + device.value = e; + } + + const toggleDarkMode = (e) => { + config.darkMode = e + } + + const toggleDarkModeAuto = () =>{ + // 处理浏览器主题 + const darkQuery = window.matchMedia('(prefers-color-scheme: dark)') + const dark = darkQuery.matches + toggleTheme(dark) + darkQuery.addEventListener('change', (e) => { + toggleTheme(e.matches) + }) + } + + const toggleConfigSideWidth = (e) => { + config.layout_side_width = e; + } + + const toggleConfigSideCollapsedWidth = (e) => { + config.layout_side_collapsed_width = e; + } + + const toggleConfigSideItemHeight = (e) => { + config.layout_side_item_height = e; + } + + const toggleConfigWatermark = (e) => { + config.show_watermark = e; + } + + const toggleSideModel= (e) =>{ + config.side_mode = e + } + + watchEffect(() => { + if (theme.value === 'dark') { + document.documentElement.classList.add('dark'); + document.documentElement.classList.remove('light'); + } else { + document.documentElement.classList.add('light'); + document.documentElement.classList.remove('dark'); + } + }) + watchEffect(() => { + // 色弱模式监听处理 + if (config.weakness) { + document.documentElement.classList.add('html-weakenss'); + } else { + document.documentElement.classList.remove('html-weakenss'); + } + }) + watchEffect(() => { + // 灰色模式监听处理 + if (config.grey) { + document.documentElement.classList.add('html-grey'); + } else { + document.documentElement.classList.remove('html-grey'); + } + }) + + + + watchEffect(() => { + if(config.darkMode === 'auto'){ + toggleDarkModeAuto() + } + + if(config.darkMode === 'dark'){ + toggleTheme(true) + }else{ + toggleTheme(false) + } + }) + + watchEffect(() => { + setBodyPrimaryColor(config.primaryColor, theme.value) + }) + + return { + theme, + device, + config, + toggleTheme, + toggleDevice, + toggleWeakness, + toggleGrey, + togglePrimaryColor, + toggleTabs, + toggleDarkMode, + toggleConfigSideWidth, + toggleConfigSideCollapsedWidth, + toggleConfigSideItemHeight, + toggleConfigWatermark, + toggleSideModel + } + +}) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/dictionary.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/dictionary.js new file mode 100644 index 000000000..cd88f8c72 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/dictionary.js @@ -0,0 +1,40 @@ +import { findSysDictionary } from '@/api/sysDictionary' + +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useDictionaryStore = defineStore('dictionary', () => { + const dictionaryMap = ref({}) + + const setDictionaryMap = (dictionaryRes) => { + dictionaryMap.value = { ...dictionaryMap.value, ...dictionaryRes } + } + + const getDictionary = async(type) => { + if (dictionaryMap.value[type] && dictionaryMap.value[type].length) { + return dictionaryMap.value[type] + } else { + const res = await findSysDictionary({ type }) + if (res.code === 0) { + const dictionaryRes = {} + const dict = [] + res.data.resysDictionary.sysDictionaryDetails && res.data.resysDictionary.sysDictionaryDetails.forEach(item => { + dict.push({ + label: item.label, + value: item.value, + extend: item.extend + }) + }) + dictionaryRes[res.data.resysDictionary.type] = dict + setDictionaryMap(dictionaryRes) + return dictionaryMap.value[type] + } + } + } + + return { + dictionaryMap, + setDictionaryMap, + getDictionary + } +}) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/router.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/router.js new file mode 100644 index 000000000..a4bb9a45f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/router.js @@ -0,0 +1,147 @@ +import { asyncRouterHandle } from '@/utils/asyncRouter' +import { emitter } from '@/utils/bus.js' +import { asyncMenu } from '@/api/menu' +import { defineStore } from 'pinia' +import { ref,watchEffect } from 'vue' +import pathInfo from "@/pathInfo.json"; + +const notLayoutRouterArr = [] +const keepAliveRoutersArr = [] +const nameMap = {} + +const formatRouter = (routes, routeMap, parent) => { + routes && routes.forEach(item => { + item.parent = parent + item.meta.btns = item.btns + item.meta.hidden = item.hidden + if (item.meta.defaultMenu === true) { + if (!parent) { + item = { ...item, path: `/${item.path}` } + notLayoutRouterArr.push(item) + } + } + routeMap[item.name] = item + if (item.children && item.children.length > 0) { + formatRouter(item.children, routeMap, item) + } + }) +} + +const KeepAliveFilter = (routes) => { + routes && routes.forEach(item => { + // 子菜单中有 keep-alive 的,父菜单也必须 keep-alive,否则无效。这里将子菜单中有 keep-alive 的父菜单也加入。 + if ((item.children && item.children.some(ch => ch.meta.keepAlive) || item.meta.keepAlive)) { + const path = item.meta.path + keepAliveRoutersArr.push(pathInfo[path]) + nameMap[item.name] = pathInfo[path] + } + if (item.children && item.children.length > 0) { + KeepAliveFilter(item.children) + } + }) +} + +export const useRouterStore = defineStore('router', () => { + const keepAliveRouters = ref([]) + const asyncRouterFlag = ref(0) + const setKeepAliveRouters = (history) => { + const keepArrTemp = [] + history.forEach(item => { + if (nameMap[item.name]) { + keepArrTemp.push(nameMap[item.name]) + } + }) + keepAliveRouters.value = Array.from(new Set(keepArrTemp)) + } + emitter.on('setKeepAlive', setKeepAliveRouters) + + const asyncRouters = ref([]) + + const topMenu = ref([]) + + const leftMenu = ref([]) + + const menuMap = {} + + const topActive = ref("") + + + + + + const setLeftMenu = (name) => { + sessionStorage.setItem('topActive', name) + topActive.value = name + if(menuMap[name]?.children){ + leftMenu.value = menuMap[name].children + } + return menuMap[name]?.children + } + + watchEffect(()=>{ + let topActive = sessionStorage.getItem("topActive") + let firstHasChildren = '' + asyncRouters.value[0]?.children.forEach((item) => { + if (item.hidden) return; + menuMap[item.name] = item; + if (!firstHasChildren && item.children && item.children.length > 0) { + firstHasChildren = item.name + } + topMenu.value.push({...item, children: []}) + }); + + if(!menuMap[topActive]?.children && firstHasChildren){ + topActive = firstHasChildren + } + setLeftMenu(topActive) + }) + + const routeMap = ({}) + // 从后台获取动态路由 + const SetAsyncRouter = async() => { + asyncRouterFlag.value++ + const baseRouter = [{ + path: '/layout', + name: 'layout', + component: 'view/layout/index.vue', + meta: { + title: '底层layout' + }, + children: [] + }] + const asyncRouterRes = await asyncMenu() + const asyncRouter = asyncRouterRes.data.menus + asyncRouter && asyncRouter.push({ + path: 'reload', + name: 'Reload', + hidden: true, + meta: { + title: '', + closeTab: true, + }, + component: 'view/error/reload.vue' + }) + formatRouter(asyncRouter, routeMap) + baseRouter[0].children = asyncRouter + if (notLayoutRouterArr.length !== 0) { + baseRouter.push(...notLayoutRouterArr) + } + asyncRouterHandle(baseRouter) + KeepAliveFilter(asyncRouter) + asyncRouters.value = baseRouter + return true + } + + return { + topActive, + setLeftMenu, + topMenu, + leftMenu, + asyncRouters, + keepAliveRouters, + asyncRouterFlag, + SetAsyncRouter, + routeMap + } +}) + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/user.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/user.js new file mode 100644 index 000000000..a803b857a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/pinia/modules/user.js @@ -0,0 +1,143 @@ +import { login, getUserInfo, setSelfInfo } from '@/api/user' +import { jsonInBlacklist } from '@/api/jwt' +import router from '@/router/index' +import { ElLoading, ElMessage } from 'element-plus' +import { defineStore } from 'pinia' +import { ref, computed, watch } from 'vue' +import { useRouterStore } from './router' +import cookie from 'js-cookie' + +import {useAppStore} from "@/pinia"; + +export const useUserStore = defineStore('user', () => { + const appStore = useAppStore() + const loadingInstance = ref(null) + + const userInfo = ref({ + uuid: '', + nickName: '', + headerImg: '', + authority: {}, + }) + const token = ref(window.localStorage.getItem('token') || cookie.get('x-token') || '') + const setUserInfo = (val) => { + userInfo.value = val + if(val.originSetting){ + Object.keys(appStore.config).forEach(key => { + appStore.config[key] = val.originSetting[key] + }) + } + } + + const setToken = (val) => { + token.value = val + } + + const NeedInit = async () => { + token.value = '' + window.localStorage.removeItem('token') + await router.push({name: 'Init', replace: true}) + } + + const ResetUserInfo = (value = {}) => { + userInfo.value = { + ...userInfo.value, + ...value + } + } + /* 获取用户信息*/ + const GetUserInfo = async() => { + const res = await getUserInfo() + if (res.code === 0) { + setUserInfo(res.data.userInfo) + } + return res + } + /* 登录*/ + const LoginIn = async(loginInfo) => { + loadingInstance.value = ElLoading.service({ + fullscreen: true, + text: '登录中,请稍候...', + }) + + const res = await login(loginInfo) + + // 登陆失败,直接返回 + if (res.code !== 0) { + loadingInstance.value.close() + return false + } + + // 登陆成功,设置用户信息和权限相关信息 + setUserInfo(res.data.user) + setToken(res.data.token) + + // 初始化路由信息 + const routerStore = useRouterStore() + await routerStore.SetAsyncRouter() + const asyncRouters = routerStore.asyncRouters + + // 注册到路由表里 + asyncRouters.forEach(asyncRouter => { + router.addRoute(asyncRouter) + }) + + if (!router.hasRoute(userInfo.value.authority.defaultRouter)) { + ElMessage.error('请联系管理员进行授权') + } else { + await router.replace({ name: userInfo.value.authority.defaultRouter }) + } + + const isWin = ref(/windows/i.test(navigator.userAgent)) + if (isWin.value) { + window.localStorage.setItem('osType', 'WIN') + } else { + window.localStorage.setItem('osType', 'MAC') + } + + + // 全部操作均结束,关闭loading并返回 + loadingInstance.value.close() + return true + } + /* 登出*/ + const LoginOut = async() => { + const res = await jsonInBlacklist() + + // 登出失败 + if (res.code !== 0) { + return + } + + await ClearStorage() + + // 把路由定向到登录页,无需等待直接reload + router.push({ name: 'Login', replace: true }) + window.location.reload() + } + /* 清理数据 */ + const ClearStorage = async() => { + token.value = '' + sessionStorage.clear() + window.localStorage.removeItem('token') + cookie.remove('x-token') + localStorage.removeItem('originSetting') + } + + watch(() => token.value, () => { + window.localStorage.setItem('token', token.value) + }) + + return { + userInfo, + token, + NeedInit, + ResetUserInfo, + GetUserInfo, + LoginIn, + LoginOut, + setToken, + loadingInstance, + ClearStorage + } +}) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/api/info.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/api/info.js new file mode 100644 index 000000000..e0b5fec7c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/api/info.js @@ -0,0 +1,110 @@ +import service from '@/utils/request' + +// @Tags Info +// @Summary 创建公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Info true "创建公告" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /info/createInfo [post] +export const createInfo = (data) => { + return service({ + url: '/info/createInfo', + method: 'post', + data + }) +} + +// @Tags Info +// @Summary 删除公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Info true "删除公告" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /info/deleteInfo [delete] +export const deleteInfo = (params) => { + return service({ + url: '/info/deleteInfo', + method: 'delete', + params + }) +} + +// @Tags Info +// @Summary 批量删除公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除公告" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /info/deleteInfo [delete] +export const deleteInfoByIds = (params) => { + return service({ + url: '/info/deleteInfoByIds', + method: 'delete', + params + }) +} + +// @Tags Info +// @Summary 更新公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body model.Info true "更新公告" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /info/updateInfo [put] +export const updateInfo = (data) => { + return service({ + url: '/info/updateInfo', + method: 'put', + data + }) +} + +// @Tags Info +// @Summary 用id查询公告 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query model.Info true "用id查询公告" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /info/findInfo [get] +export const findInfo = (params) => { + return service({ + url: '/info/findInfo', + method: 'get', + params + }) +} + +// @Tags Info +// @Summary 分页获取公告列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query request.PageInfo true "分页获取公告列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /info/getInfoList [get] +export const getInfoList = (params) => { + return service({ + url: '/info/getInfoList', + method: 'get', + params + }) +} +// @Tags Info +// @Summary 获取数据源 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /info/findInfoDataSource [get] +export const getInfoDataSource = () => { + return service({ + url: '/info/getInfoDataSource', + method: 'get', + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/form/info.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/form/info.vue new file mode 100644 index 000000000..95a868d76 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/form/info.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/view/info.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/view/info.vue new file mode 100644 index 000000000..3643b897d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/announcement/view/info.vue @@ -0,0 +1,418 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/email/api/email.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/email/api/email.js new file mode 100644 index 000000000..590c862f4 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/email/api/email.js @@ -0,0 +1,30 @@ +import service from '@/utils/request' +// @Tags System +// @Summary 发送测试邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" +// @Router /email/emailTest [post] +export const emailTest = (data) => { + return service({ + url: '/email/emailTest', + method: 'post', + data + }) +} + +// @Tags System +// @Summary 发送邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body email_response.Email true "发送邮件必须的参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" +// @Router /email/sendEmail [post] +export const sendEmail = (data) => { + return service({ + url: '/email/sendEmail', + method: 'post', + data + }) +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/email/view/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/email/view/index.vue new file mode 100644 index 000000000..11d57e44f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/plugin/email/view/index.vue @@ -0,0 +1,63 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/router/index.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/router/index.js new file mode 100644 index 000000000..52d376307 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/router/index.js @@ -0,0 +1,31 @@ +import { createRouter, createWebHashHistory } from 'vue-router' + +const routes = [{ + path: '/', + redirect: '/login' +}, +{ + path: '/init', + name: 'Init', + component: () => import('@/view/init/index.vue') +}, +{ + path: '/login', + name: 'Login', + component: () => import('@/view/login/index.vue') +}, +{ + path: '/:catchAll(.*)', + meta: { + closeTab: true, + }, + component: () => import('@/view/error/index.vue') +} +] + +const router = createRouter({ + history: createWebHashHistory(), + routes, +}) + +export default router diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/element/index.scss b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/element/index.scss new file mode 100644 index 000000000..40ef0df5c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/element/index.scss @@ -0,0 +1,24 @@ +@forward 'element-plus/theme-chalk/src/common/var.scss' with ( + $colors: ( + 'white': #ffffff, + 'black': #000000, + 'primary': ( + 'base': #4d70ff, + ), + 'success': ( + 'base': #67c23a, + ), + 'warning': ( + 'base': #e6a23c, + ), + 'danger': ( + 'base': #f56c6c, + ), + 'error': ( + 'base': #f56c6c, + ), + 'info': ( + 'base': #909399, + ), + ) +); diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/element_visiable.scss b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/element_visiable.scss new file mode 100644 index 000000000..e85342c6c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/element_visiable.scss @@ -0,0 +1,137 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; +@import '@/style/main.scss'; +@import "@/style/reset"; + +.el-button { + font-weight: 400; + border-radius: 2px; +} + +.gva-pagination { + @apply flex justify-end; + .el-pagination__editor { + .el-input__inner { + @apply h-8; + } + } + + .is-active { + @apply rounded text-white; + background: var(--el-color-primary); + color: #ffffff !important; + } +} + + +.el-drawer__header{ + margin-bottom: 0 !important; + padding-top: 16px !important; + padding-bottom: 16px !important; + @apply border-0 border-b border-solid border-gray-200; +} + + +.el-form--inline { + .el-form-item { + & > .el-input, .el-cascader, .el-select, .el-date-editor, .el-autocomplete { + @apply w-52; + } + } +} + +.el-dropdown { + @apply overflow-hidden +} + + +.el-table { + tr{ + th { + @apply dark:bg-slate-900; + .cell { + @apply leading-[36px] text-gray-700 dark:text-gray-200; + } + } + } + .el-table__row { + td { + @apply dark:bg-slate-900; + .cell { + @apply leading-[32px] text-gray-600 dark:text-gray-300; + } + } + } + tr{ + th{ + &.is-leaf { + @apply dark:bg-slate-900; + } + } + + } +} + +// layout + +// table +.el-pagination { + @apply mt-8; + .btn-prev, + .btn-next { + @apply border border-solid border-gray-300 dark:border-gray-700 rounded; + } + .el-pager { + li { + @apply border border-solid border-gray-300 dark:border-gray-600 rounded text-gray-600 text-sm mx-1; + } + } +} +.el-menu{ + li{ + @apply my-1; + } +} +.el-menu--vertical{ + .el-menu-item{ + border-radius: 2px; + &.is-active{ + background-color: var(--el-color-primary) !important; + color: #fff !important; + } + } +} + + +.el-sub-menu.el-sub-menu__hide-arrow{ + height: 44px; +} + + +.el-tabs__header{ + margin: 0 0 1px !important; +} + +.el-sub-menu.is-active{ + > .el-sub-menu__title{ + color: var(--el-color-primary) !important; + } +} + +.el-sub-menu__title.el-tooltip__trigger, +.el-menu-item .el-menu-tooltip__trigger{ + justify-content: center; +} + +.el-menu--horizontal .el-menu .el-sub-menu__title{ + justify-content: flex-start; +} + +html.dark { + /* 自定义深色背景颜色 */ + --el-bg-color: rgb(30 ,41 ,59); + --el-bg-color-overlay: rgb(40 ,51 ,69); + --el-fill-color-light: rgb(15 ,23 ,42); + --el-fill-color : rgb(15 ,23 ,42); +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/iconfont.css b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/iconfont.css new file mode 100644 index 000000000..bc091a05c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/iconfont.css @@ -0,0 +1,47 @@ +@font-face { + font-family: 'gvaIcon'; + src: url('data:font/ttf;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTZJUyU8AAA14AAAAHEdERUYAKQARAAANWAAAAB5PUy8yPJpJTAAAAVgAAABgY21hcM0T0L4AAAHYAAABWmdhc3D//wADAAANUAAAAAhnbHlmRk3UvwAAA0wAAAbYaGVhZB/a5jgAAADcAAAANmhoZWEHngOFAAABFAAAACRobXR4DaoBrAAAAbgAAAAebG9jYQbMCGgAAAM0AAAAGG1heHABGgB+AAABOAAAACBuYW1lXoIBAgAACiQAAAKCcG9zdN15OnUAAAyoAAAAqAABAAAAAQAA+a916l8PPPUACwQAAAAAAN5YUSMAAAAA3lhRIwBL/8ADwAM1AAAACAACAAAAAAAAAAEAAAOA/4AAXAQAAAAAAAPAAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAALAHIABQAAAAAAAgAAAAoACgAAAP8AAAAAAAAABAQAAZAABQAAAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZADA5mXmfQOA/4AAAAPcAIAAAAABAAAAAAAAAAAAAAAgAAEEAAAAAAAAAAQAAAAEAACLAIoAYAB1AHYASwBLAGAAAAAAAAMAAAADAAAAHAABAAAAAABUAAMAAQAAABwABAA4AAAACgAIAAIAAuZm5mrmduZ9//8AAOZl5mrmdeZ7//8ZnhmbGZEZjQABAAAAAAAAAAAAAAAAAQYAAAEAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEYAigEcAbgCUAK6AxoDbAACAIsAIANsAswAEQAjAAAlIicBJjQ3ATYeAQYHCQEeAQYhIicBJjQ3ATYeAQYHCQEeAQYDSw0J/qsLCwFVChsSAgr+xAE8CgIV/qkNCP6qCgoBVgkbEgIK/sUBOwoCFCAJATULGQsBNQoCExwI/uL+4ggbFAkBNQsZCwE1CgITHAj+4v7iCRoUAAAAAAIAigAgA2sCzAARACIAAAE0JwEmDgEWFwkBDgEWMjcBNiUBJg4BFhcJAQ4BFjI3ATY0AiAL/qsJHBECCQE8/sQJAhQZCQFVCwFA/qsKGxICCgE8/sQKAhUZCQFVCwF1DQsBNQoCExwI/uL+4gkaFAkBNQskATUKAhMcCP7i/uIJGhQJATULGQADAGD/wAOgAzUATABcAGwAAAE1NCcmJyYiBwYHBh0BDgEdARQWOwEyNj0BNCYrATU0NzY3NjIXFhcWHQEjIgYdARQWOwEGBwYHLgEjIgYUFjMyNjc2NzY3PgE9ATQmBRUUBisBIiY9ATQ2OwEyFgUUBisBIiY9ATQ2OwEyFhUDYDAvT1O+U08vMBslLB9VHi0tHiAoJkFDnENBJiggHi0tHhUPJC5SChwRHCQkHBEeCHJAMxAfKiX9kAYFVQUGBgVVBQYCVQYFVQUGBgVVBQYByQxgUlAuMDAuUFJgDAQqG6seLCweqx4tCk5DQScnJydBQ04KLR6rHiwrGiAGDxElNiUSEAc1KkUBKx6rGyhFqwQGBgSrBQYGsAQGBgSrBQYGBQAABAB1//UDjQMLABsANwBSAHEAABMyNj0BFxYyNjQvATMyNjQmKwEiBwYHBh0BFBYFIgYdAScmIgYUHwEjIgYUFjsBMjc2NzY9ATYmJQc1NCYiBh0BFBcWFxY7ATI2NCYrATc2NCYGATQ1FSYnJisBIgYUFjsBBwYUFjI/ARUUFjI2PQEnJpUNE7wJHRMKvIcMFBQM1ggCDAgCFALiDRPJCRoTCcmJDBQUDNYIAg8CAwES/gbJExkUAggKBAbWDBQUDInJCRMXAgEHCwQG2AwUFAyJvAkSHgi8ExoTAgEB9RQMibwIEhkKvBMZFAIGDAQI1gwU6hQMickJExoJyRMZFAIICgQG2AwUIsmHDBQUDNYIAg8CAxQZE8kKGRMBAcABAQIOAwMUGRO8ChkTCbyHDBQUDNYFBAAABAB2//cDjgMMABoANQBRAG0AAAEjIgYUFjsBMjc2NzY9ATQmIgYdAScmIgYUFwEzMjY0JisBIgcGBwYdARQWMjY9ARcWMjY0JyUmJyYrASIGFBY7AQcGFBYyPwEVFBYyNj0BLgE3FhcWOwEyNjQmKwE3NjQmIg8BNTQmIgYdAR4BATqJDRMTDdUJAg8CAhMaE7cKGRQKAjeJDRMTDdUJAg8CAhMaE8gJHhIK/i8HCgQH1w0TEw2JyQoTHQnIFBkTAQKoBwoEBtYNExMNibwKFBkKvBMZFAICAhoUGRMCBwoEBtYNExMNib4KExoK/iAUGRMCBwoEB9UNExMNickIEhkK8w8CAhMZFMgKGRMJyYkNExMN1QIJzQ8CAhMZFLsKGhMKvIkNExMN1QMIAAAAAAUAS//LA7UDNQAUACkAKgA3AEQAAAEiBwYHBhQXFhcWMjc2NzY0JyYnJgMiJyYnJjQ3Njc2MhcWFxYUBwYHBgMjFB4BMj4BNC4BIg4BFyIGHQEUFjI2PQE0JgIAd2ZiOzs7O2Jm7mZiOzs7O2Jmd2VXVDIzMzJUV8pXVDIzMzJUV2UrDBQWFAwMFBYUDCsNExMaExMDNTs7YmbuZmI7Ozs7YmbuZmI7O/zWMzJUV8pXVDIzMzJUV8pXVDIzAjULFAwMFBYUDAwUgBQM6w0TEw3rDBQAAQBL/+ADwAMgAD0AAAEmBg8BLgEjIgcGBwYUFxYXFjMyPgE3Ni4BBgcOAiMiJyYnJjQ3Njc2MzIeARcnJg4BFh8BMj8BNj8BNCYDpgwXAxc5yXZyY184Ojo4X2NyWaB4HgULGhcFGWaJS2FUUTAwMTBRU2FIhGQbgA0WBw4NwgUIBAwDMQ0CsQMODFhmeDk3XmHiYV43OUV9UQ0XCQsMRWo6MC9PUr9TTy8wNmNBJQMOGhYDMwMBCAu6DRYAAAAAAgBg/8YDugMiAB4AMwAABSc+ATU0JyYnJiIHBgcGFBcWFxYzMjc2NxcWMjc2JiUiJyYnJjQ3Njc2MhcWFxYUBwYHBgOxviouNDFVV8lXVTIzMzJVV2RDPzwzvgkeCAcB/hxUSEYpKiopRkioSEYpKyspRkgCvjB9RGRYVDIzNDJVWMlXVTE0GBYqvgkJChuBKylGSKhIRikqKilGSKhIRikrAAAAABIA3gABAAAAAAAAABMAKAABAAAAAAABAAgATgABAAAAAAACAAcAZwABAAAAAAADAAgAgQABAAAAAAAEAAgAnAABAAAAAAAFAAsAvQABAAAAAAAGAAgA2wABAAAAAAAKACsBPAABAAAAAAALABMBkAADAAEECQAAACYAAAADAAEECQABABAAPAADAAEECQACAA4AVwADAAEECQADABAAbwADAAEECQAEABAAigADAAEECQAFABYApQADAAEECQAGABAAyQADAAEECQAKAFYA5AADAAEECQALACYBaABDAHIAZQBhAHQAZQBkACAAYgB5ACAAaQBjAG8AbgBmAG8AbgB0AABDcmVhdGVkIGJ5IGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABSAGUAZwB1AGwAYQByAABSZWd1bGFyAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAABHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuAABoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAABodHRwOi8vZm9udGVsbG8uY29tAAAAAAIAAAAAAAAACgAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAgECAQMBBAEFAQYBBwEIAQkRYXJyb3ctZG91YmxlLWxlZnQSYXJyb3ctZG91YmxlLXJpZ2h0EGN1c3RvbWVyLXNlcnZpY2URZnVsbHNjcmVlbi1leHBhbmQRZnVsbHNjcmVlbi1zaHJpbmsGcHJvbXB0B3JlZnJlc2gGc2VhcmNoAAAAAf//AAIAAQAAAAwAAAAWAAAAAgABAAMACgABAAQAAAACAAAAAAAAAAEAAAAA1aQnCAAAAADeWFEjAAAAAN5YUSM=') format('truetype'); + font-weight: 600; + font-style: normal; + font-display: swap; +} +.gvaIcon { + font-family: "gvaIcon" !important; + font-size: 16px; + font-style: normal; + font-weight: 800; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + +.gvaIcon-arrow-double-left:before { + content: "\e665"; + } + +.gvaIcon-arrow-double-right:before { + content: "\e666"; +} + +.gvaIcon-fullscreen-shrink:before { + content: "\e676"; +} +.gvaIcon-customer-service:before { + content: "\e66a"; + } + +.gvaIcon-fullscreen-expand:before { + content: "\e675"; +} + +.gvaIcon-prompt:before { + content: "\e67b"; +} + +.gvaIcon-refresh:before { + content: "\e67c"; +} + +.gvaIcon-search:before { + content: "\e67d"; +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/main.scss b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/main.scss new file mode 100644 index 000000000..18c5dd906 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/main.scss @@ -0,0 +1,53 @@ + +@import '@/style/iconfont.css'; + +.html-grey{ + filter: grayscale(100%); +} + +.html-weakenss{ + filter: invert(80%); +} + +.gva-table-box { + @apply p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded my-2; + .el-table{ + @apply border-x border-t border-b-0 rounded border-table-border border-solid -mx-[1px]; + } +} + +.gva-btn-list { + @apply mb-3 flex items-center; +} + + +#nprogress .bar { + background: #29d !important; +} +.gva-customer-icon{ + @apply w-4 h-4; +} + +::-webkit-scrollbar { + @apply hidden; +} + + +.gva-search-box { + @apply p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded my-2; +} + +.gva-form-box { + @apply p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded my-2; +} + +.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content{ + background: var(--el-color-primary-bg) !important; +} + +.el-dropdown{ + outline: none; + *{ + outline: none; + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/reset.scss b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/reset.scss new file mode 100644 index 000000000..d70c71fec --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/style/reset.scss @@ -0,0 +1,508 @@ +/* Document + ========================================================================== */ + + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +@import '@/style/iconfont.css'; +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ +} + + +/* Sections + ========================================================================== */ + + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + + +/* Grouping content + ========================================================================== */ + + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; + /* 1 */ + height: 0; + /* 1 */ + overflow: visible; + /* 2 */ +} + + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + + +/* Text-level semantics + ========================================================================== */ + + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; + /* 1 */ + text-decoration: underline; + /* 2 */ + text-decoration: underline dotted; + /* 2 */ +} + + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + + +/* Embedded content + ========================================================================== */ + + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + + +/* Forms + ========================================================================== */ + + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ +} + + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { + /* 1 */ + overflow: visible; +} + + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { + /* 1 */ + text-transform: none; +} + + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; + /* 1 */ + color: inherit; + /* 2 */ + display: table; + /* 1 */ + max-width: 100%; + /* 1 */ + padding: 0; + /* 3 */ + white-space: normal; + /* 1 */ +} + + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ +} + + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + + +/* Interactive + ========================================================================== */ + + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + + +/* Misc + ========================================================================== */ + + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} + +HTML, +body, +div, +ul, +ol, +dl, +li, +dt, +dd, +p, +blockquote, +pre, +form, +fieldset, +table, +th, +td { + border: none; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; + font-size: 14px; + margin: 0px; + padding: 0px; +} + +html, +body { + height: 100%; + width: 100%; +} + +address, +caption, +cite, +code, +th, +var { + font-style: normal; + font-weight: normal; +} + +a { + text-decoration: none; +} + +input::-ms-clear { + display: none; +} + +input::-ms-reveal { + display: none; +} + +input { + -webkit-appearance: none; + margin: 0; + outline: none; + padding: 0; +} + +input::-webkit-input-placeholder { + color: #ccc; +} + +input::-ms-input-placeholder { + color: #ccc; +} + +input::-moz-placeholder { + color: #ccc; +} + +input[type=submit], +input[type=button] { + cursor: pointer; +} + +button[disabled], +input[disabled] { + cursor: default; +} + +img { + border: none; +} + +ul, +ol, +li { + list-style-type: none; +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/asyncRouter.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/asyncRouter.js new file mode 100644 index 000000000..26c3df0a0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/asyncRouter.js @@ -0,0 +1,32 @@ +const viewModules = import.meta.glob('../view/**/*.vue') +const pluginModules = import.meta.glob('../plugin/**/*.vue') + +export const asyncRouterHandle = (asyncRouter) => { + asyncRouter.forEach(item => { + if (item.component && typeof item.component === 'string') { + item.meta.path = "/src/"+item.component + if (item.component.split('/')[0] === 'view') { + item.component = dynamicImport(viewModules, item.component) + } else if (item.component.split('/')[0] === 'plugin') { + item.component = dynamicImport(pluginModules, item.component) + } + } + if (item.children) { + asyncRouterHandle(item.children) + } + }) +} + +function dynamicImport( + dynamicViewsModules, + component +) { + const keys = Object.keys(dynamicViewsModules) + const matchKeys = keys.filter((key) => { + const k = key.replace('../', '') + return k === component + }) + const matchKey = matchKeys[0] + + return dynamicViewsModules[matchKey] +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/btnAuth.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/btnAuth.js new file mode 100644 index 000000000..f94fa9bcb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/btnAuth.js @@ -0,0 +1,6 @@ +import { useRoute } from 'vue-router' +import { reactive } from 'vue' +export const useBtnAuth = () => { + const route = useRoute() + return route.meta.btns || reactive({}) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/bus.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/bus.js new file mode 100644 index 000000000..4b673fa2f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/bus.js @@ -0,0 +1,6 @@ + +// using ES6 modules +import mitt from 'mitt' + +export const emitter = mitt() + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/closeThisPage.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/closeThisPage.js new file mode 100644 index 000000000..b2a0c051c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/closeThisPage.js @@ -0,0 +1,5 @@ +import { emitter } from '@/utils/bus.js' + +export const closeThisPage = () => { + emitter.emit('closeThisPage') +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/date.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/date.js new file mode 100644 index 000000000..6bb8570bf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/date.js @@ -0,0 +1,30 @@ +// 对Date的扩展,将 Date 转化为指定格式的String +// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, +// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) +// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 +// (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 +// eslint-disable-next-line no-extend-native +Date.prototype.Format = function(fmt) { + var o = { + 'M+': this.getMonth() + 1, // 月份 + 'd+': this.getDate(), // 日 + 'h+': this.getHours(), // 小时 + 'm+': this.getMinutes(), // 分 + 's+': this.getSeconds(), // 秒 + 'q+': Math.floor((this.getMonth() + 3) / 3), // 季度 + 'S': this.getMilliseconds() // 毫秒 + } + if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length)) } + for (var k in o) { + if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) } + } + return fmt +} + +export function formatTimeToStr(times, pattern) { + var d = new Date(times).Format('yyyy-MM-dd hh:mm:ss') + if (pattern) { + d = new Date(times).Format(pattern) + } + return d.toLocaleString() +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/dictionary.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/dictionary.js new file mode 100644 index 000000000..440fe5e1a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/dictionary.js @@ -0,0 +1,35 @@ +import { useDictionaryStore } from '@/pinia/modules/dictionary' +import { getSysParam } from '@/api/sysParams' +// 获取字典方法 使用示例 getDict('sex').then(res) 或者 async函数下 const res = await getDict('sex') +export const getDict = async(type) => { + const dictionaryStore = useDictionaryStore() + await dictionaryStore.getDictionary(type) + return dictionaryStore.dictionaryMap[type] +} + +// 字典文字展示方法 +export const showDictLabel = ( + dict, + code, + keyCode = 'value', + valueCode = 'label' +) => { + if (!dict) { + return '' + } + const dictMap = {} + dict.forEach(item => { + if (Reflect.has(item, keyCode) && Reflect.has(item, valueCode)) { + dictMap[item[keyCode]] = item[valueCode] + } + }) + return Reflect.has(dictMap, code) ? dictMap[code] : '' +} + + +export const getParams = async (key)=>{ + const res = await getSysParam({key}) + if(res.code === 0){ + return res.data.value + } +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/doc.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/doc.js new file mode 100644 index 000000000..55a39490c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/doc.js @@ -0,0 +1,3 @@ +export const toDoc = (url) => { + window.open(url, '_blank') +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/downloadImg.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/downloadImg.js new file mode 100644 index 000000000..93fb2222d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/downloadImg.js @@ -0,0 +1,19 @@ +export const downloadImage = (imgsrc, name) => { // 下载图片地址和图片名 + var image = new Image() + image.setAttribute('crossOrigin', 'anonymous') + image.onload = function() { + var canvas = document.createElement('canvas') + canvas.width = image.width + canvas.height = image.height + var context = canvas.getContext('2d') + context.drawImage(image, 0, 0, image.width, image.height) + var url = canvas.toDataURL('image/png') // 得到图片的base64编码数据 + + var a = document.createElement('a') // 生成一个a元素 + var event = new MouseEvent('click') // 创建一个单击事件 + a.download = name || 'photo' // 设置图片名称 + a.href = url // 将生成的URL设置为a.href属性 + a.dispatchEvent(event) // 触发a的单击事件 + } + image.src = imgsrc +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/event.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/event.js new file mode 100644 index 000000000..5063d8cba --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/event.js @@ -0,0 +1,29 @@ +export function addEventListen( + target, + event, + handler, + capture = false +) { + if ( + target.addEventListener && + typeof target.addEventListener === 'function' + ) { + target.addEventListener(event, handler, capture); + } +} + +export function removeEventListen( + target, + event, + handler, + capture = false +) { + if ( + target.removeEventListener && + typeof target.removeEventListener === 'function' + ) { + target.removeEventListener(event, handler, capture); + } +} + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/fmtRouterTitle.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/fmtRouterTitle.js new file mode 100644 index 000000000..bcaeb676c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/fmtRouterTitle.js @@ -0,0 +1,13 @@ +export const fmtTitle = (title, now) => { + const reg = /\$\{(.+?)\}/ + const reg_g = /\$\{(.+?)\}/g + const result = title.match(reg_g) + if (result) { + result.forEach((item) => { + const key = item.match(reg)[1] + const value = now.params[key] || now.query[key] + title = title.replace(item, value) + }) + } + return title +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/format.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/format.js new file mode 100644 index 000000000..0cf75dc63 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/format.js @@ -0,0 +1,139 @@ +import { formatTimeToStr } from '@/utils/date' +import { getDict } from '@/utils/dictionary' +import {ref} from "vue"; + +export const formatBoolean = (bool) => { + if (bool !== null) { + return bool ? '是' : '否' + } else { + return '' + } +} +export const formatDate = (time) => { + if (time !== null && time !== '') { + var date = new Date(time) + return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss') + } else { + return '' + } +} + +export const filterDict = (value, options) => { + const rowLabel = options && options.filter(item => item.value === value) + return rowLabel && rowLabel[0] && rowLabel[0].label +} + +export const filterDataSource = (dataSource, value) => { + if (Array.isArray(value)) { + return value.map(item => { + const rowLabel = dataSource && dataSource.find(i => i.value === item) + return rowLabel?.label + }) + } + const rowLabel = dataSource && dataSource.find(item => item.value === value) + return rowLabel?.label +} + +export const getDictFunc = async(type) => { + const dicts = await getDict(type) + return dicts +} + +const path = import.meta.env.VITE_BASE_PATH + ':' + import.meta.env.VITE_SERVER_PORT + '/' +export const ReturnArrImg = (arr) => { + const imgArr = [] + if (arr instanceof Array) { // 如果是数组类型 + for (const arrKey in arr) { + if (arr[arrKey].slice(0, 4) !== 'http') { + imgArr.push(path + arr[arrKey]) + } else { + imgArr.push(arr[arrKey]) + } + } + } else { // 如果不是数组类型 + if (arr.slice(0, 4) !== 'http') { + imgArr.push(path + arr) + } else { + imgArr.push(arr) + } + } + return imgArr +} + +export const returnArrImg = ReturnArrImg + +export const onDownloadFile = (url) => { + window.open(path + url) +} +const colorToHex = u=>{ + let e = u.replace("#", "").match(/../g); + for (let t = 0; t < 3; t++) + e[t] = parseInt(e[t], 16); + return e +} + +const hexToColor = (u,e,t)=>{ + let a = [u.toString(16), e.toString(16), t.toString(16)]; + for (let n = 0; n < 3; n++) + a[n].length === 1 && (a[n] = `0${a[n]}`); + return `#${a.join("")}` +} +const generateAllColors = (u,e)=> { + let t = colorToHex(u); + const target = [10, 10, 30]; + for (let a = 0; a < 3; a++) + t[a] = Math.floor(t[a] * (1 - e) + target[a] * e); + return hexToColor(t[0], t[1], t[2]) +} + +const generateAllLightColors = (u, e) => { + let t = colorToHex(u); + const target = [240, 248, 255]; // RGB for blue white color + for (let a = 0; a < 3; a++) + t[a] = Math.floor(t[a] * (1 - e) + target[a] * e); + return hexToColor(t[0], t[1], t[2]); +} + + +function addOpacityToColor(u, opacity) { + let t = colorToHex(u); + return `rgba(${t[0]}, ${t[1]}, ${ t[2]}, ${opacity})`; +} + + +export const setBodyPrimaryColor = (primaryColor, darkMode) =>{ + + let fmtColorFunc = generateAllColors + if (darkMode === 'light') { + fmtColorFunc = generateAllLightColors + } + + document.documentElement.style.setProperty('--el-color-primary', primaryColor) + document.documentElement.style.setProperty('--el-color-primary-bg', addOpacityToColor(primaryColor, 0.4)) + for (let times = 1; times <= 2; times++) { + document.documentElement.style.setProperty(`--el-color-primary-dark-${times}`, fmtColorFunc(primaryColor, times / 10)) + } + for (let times = 1; times <= 10; times++) { + document.documentElement.style.setProperty(`--el-color-primary-light-${times}`, fmtColorFunc(primaryColor, times / 10)) + } + document.documentElement.style.setProperty(`--el-menu-hover-bg-color`, addOpacityToColor(primaryColor, 0.2)) +} + + +const baseUrl = ref(import.meta.env.VITE_BASE_API) + +export const getBaseUrl = () => { + return baseUrl.value === "/" ? "" : baseUrl.value +} + +export const CreateUUID = () => { + let d = new Date().getTime() + if (window.performance && typeof window.performance.now === 'function') { + d += performance.now() + } + return '00000000-0000-0000-0000-000000000000'.replace(/0/g, (c) => { + const r = (d + Math.random() * 16) % 16 | 0 // d是随机种子 + d = Math.floor(d / 16) + return (c === '0' ? r : (r & 0x3 | 0x8)).toString(16) + }) +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/image.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/image.js new file mode 100644 index 000000000..9a929588c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/image.js @@ -0,0 +1,113 @@ +export default class ImageCompress { + constructor(file, fileSize, maxWH = 1920) { + this.file = file + this.fileSize = fileSize + this.maxWH = maxWH // 最大长宽 + } + + compress() { + // 压缩 + const fileType = this.file.type + const fileSize = this.file.size / 1024 + return new Promise(resolve => { + const reader = new FileReader() + reader.readAsDataURL(this.file) + reader.onload = () => { + const canvas = document.createElement('canvas') + const img = document.createElement('img') + img.src = reader.result + img.onload = () => { + const ctx = canvas.getContext('2d') + const _dWH = this.dWH(img.width, img.height, this.maxWH) + canvas.width = _dWH.width + canvas.height = _dWH.height + + // 清空后, 重写画布 + ctx.clearRect(0, 0, canvas.width, canvas.height) + ctx.drawImage(img, 0, 0, canvas.width, canvas.height) + + const newImgData = canvas.toDataURL(fileType, 0.90) + + // 压缩宽高后的图像大小 + const newImgSize = this.fileSizeKB(newImgData) + + if (newImgSize > this.fileSize) { + console.log('图片尺寸太大!' + fileSize + ' >> ' + newImgSize) + } + + const blob = this.dataURLtoBlob(newImgData, fileType) + const nfile = new File([blob], this.file.name) + resolve(nfile) + } + } + }) + } + + /** + * 长宽等比缩小 + * 图像的一边(长或宽)为最大目标值 + */ + dWH(srcW, srcH, dMax) { + const defaults = { + width: srcW, + height: srcH + } + if (Math.max(srcW, srcH) > dMax) { + if (srcW > srcH) { + defaults.width = dMax + defaults.height = Math.round(srcH * (dMax / srcW)) + return defaults + } else { + defaults.height = dMax + defaults.width = Math.round(srcW * (dMax / srcH)) + return defaults + } + } else { + return defaults + } + } + + fileSizeKB(dataURL) { + let sizeKB = 0 + sizeKB = Math.round((dataURL.split(',')[1].length * 3 / 4) / 1024) + return sizeKB + } + + /** + * 转为Blob + */ + dataURLtoBlob(dataURL, fileType) { + const byteString = atob(dataURL.split(',')[1]) + let mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0] + const ab = new ArrayBuffer(byteString.length) + const ia = new Uint8Array(ab) + for (let i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i) + } + if (fileType) { + mimeString = fileType + } + return new Blob([ab], { type: mimeString, lastModifiedDate: new Date() }) + } +} + +const path = import.meta.env.VITE_FILE_API +export const getUrl = (url) => { + if (url && url.slice(0, 4) !== 'http'){ + if (path === "/"){ + return url + } + if (url.slice(0, 1) === "/"){ + return path + url + } + return path + "/" + url + }else{ + return url + } +} + +export const isVideoExt = (url) => url.endsWith('.mp4') || url.endsWith('.mov') || url.endsWith('.webm') || url.endsWith('.ogg'); + +export const isVideoMime = (type) => type == 'video/mp4' || type == 'video/webm' || type == 'video/ogg'; + +export const isImageMime = (type) => type == 'image/jpeg' || type == 'image/png' || type == 'image/webp' || type == 'image/svg+xml'; diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/page.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/page.js new file mode 100644 index 000000000..6a3c6d843 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/page.js @@ -0,0 +1,9 @@ +import { fmtTitle } from '@/utils/fmtRouterTitle' +import config from '@/core/config' +export default function getPageTitle(pageTitle, route) { + if (pageTitle) { + const title = fmtTitle(pageTitle, route) + return `${title} - ${config.appName}` + } + return `${config.appName}` +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/request.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/request.js new file mode 100644 index 000000000..20c041e2b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/request.js @@ -0,0 +1,156 @@ +import axios from 'axios' // 引入axios +import { ElMessage, ElMessageBox } from 'element-plus' +import { useUserStore } from '@/pinia/modules/user' +import router from '@/router/index' +import { ElLoading } from 'element-plus' + +const service = axios.create({ + baseURL: import.meta.env.VITE_BASE_API, + timeout: 99999 +}) +let activeAxios = 0 +let timer +let loadingInstance +const showLoading = (option = { + target: null, +}) => { + const loadDom = document.getElementById('gva-base-load-dom') + activeAxios++ + if (timer) { + clearTimeout(timer) + } + timer = setTimeout(() => { + if (activeAxios > 0) { + if (!option.target) option.target = loadDom + loadingInstance = ElLoading.service(option) + } + }, 400) +} + +const closeLoading = () => { + activeAxios-- + if (activeAxios <= 0) { + clearTimeout(timer) + loadingInstance && loadingInstance.close() + } +} +// http request 拦截器 +service.interceptors.request.use( + config => { + if (!config.donNotShowLoading) { + showLoading(config.loadingOption) + } + const userStore = useUserStore() + config.headers = { + 'Content-Type': 'application/json', + 'x-token': userStore.token, + 'x-user-id': userStore.userInfo.ID, + ...config.headers + } + return config + }, + error => { + if (!error.config.donNotShowLoading) { + closeLoading() + } + ElMessage({ + showClose: true, + message: error, + type: 'error' + }) + return error + } +) + +// http response 拦截器 +service.interceptors.response.use( + response => { + const userStore = useUserStore() + if (!response.config.donNotShowLoading) { + closeLoading() + } + if (response.headers['new-token']) { + userStore.setToken(response.headers['new-token']) + } + if (response.data.code === 0 || response.headers.success === 'true') { + if (response.headers.msg) { + response.data.msg = decodeURI(response.headers.msg) + } + return response.data + } else { + ElMessage({ + showClose: true, + message: response.data.msg || decodeURI(response.headers.msg), + type: 'error' + }) + return response.data.msg ? response.data : response + } + }, + error => { + if (!error.config.donNotShowLoading) { + closeLoading() + } + + if (!error.response) { + ElMessageBox.confirm(` +

检测到请求错误

+

${error}

+ `, '请求报错', { + dangerouslyUseHTMLString: true, + distinguishCancelAndClose: true, + confirmButtonText: '稍后重试', + cancelButtonText: '取消' + }) + return + } + + switch (error.response.status) { + case 500: + ElMessageBox.confirm(` +

检测到接口错误${error}

+

错误码 500 :此类错误内容常见于后台panic,请先查看后台日志,如果影响您正常使用可强制登出清理缓存

+ `, '接口报错', { + dangerouslyUseHTMLString: true, + distinguishCancelAndClose: true, + confirmButtonText: '清理缓存', + cancelButtonText: '取消' + }) + .then(() => { + const userStore = useUserStore() + userStore.ClearStorage() + router.push({ name: 'Login', replace: true }) + }) + break + case 404: + ElMessageBox.confirm(` +

检测到接口错误${error}

+

错误码 404 :此类错误多为接口未注册(或未重启)或者请求路径(方法)与api路径(方法)不符--如果为自动化代码请检查是否存在空格

+ `, '接口报错', { + dangerouslyUseHTMLString: true, + distinguishCancelAndClose: true, + confirmButtonText: '我知道了', + cancelButtonText: '取消' + }) + break + case 401: + ElMessageBox.confirm(` +

无效的令牌

+

错误码: 401 错误信息:${error}

+ `, '身份信息', { + dangerouslyUseHTMLString: true, + distinguishCancelAndClose: true, + confirmButtonText: '重新登录', + cancelButtonText: '取消' + }) + .then(() => { + const userStore = useUserStore() + userStore.ClearStorage() + router.push({ name: 'Login', replace: true }) + }) + break + } + + return error + } +) +export default service diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/stringFun.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/stringFun.js new file mode 100644 index 000000000..eac417911 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/utils/stringFun.js @@ -0,0 +1,29 @@ +/* eslint-disable */ +export const toUpperCase = (str) => { + if (str[0]) { + return str.replace(str[0], str[0].toUpperCase()) + } else { + return '' + } +} + +export const toLowerCase = (str) => { + if (str[0]) { + return str.replace(str[0], str[0].toLowerCase()) + } else { + return '' + } +} + +// 驼峰转换下划线 +export const toSQLLine = (str) => { + if (str === 'ID') return 'ID' + return str.replace(/([A-Z])/g, "_$1").toLowerCase(); +} + +// 下划线转换驼峰 +export const toHump = (name) => { + return name.replace(/\_(\w)/g, function(all, letter) { + return letter.toUpperCase(); + }); +} \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/about/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/about/index.vue new file mode 100644 index 000000000..97fc87ff7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/about/index.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/banner.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/banner.vue new file mode 100644 index 000000000..0141f5c5f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/banner.vue @@ -0,0 +1,40 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/card.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/card.vue new file mode 100644 index 000000000..b922fc22a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/card.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts-content-numbers.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts-content-numbers.vue new file mode 100644 index 000000000..b11564f7c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts-content-numbers.vue @@ -0,0 +1,183 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts-people-numbers.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts-people-numbers.vue new file mode 100644 index 000000000..a7013f20e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts-people-numbers.vue @@ -0,0 +1,141 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts.vue new file mode 100644 index 000000000..3bcf5a9ed --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/charts.vue @@ -0,0 +1,58 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/index.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/index.js new file mode 100644 index 000000000..23cc04a76 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/index.js @@ -0,0 +1,19 @@ +import GvaBanner from "./banner.vue" +import GvaCard from "./card.vue" +import GvaChart from "./charts.vue" +import GvaTable from "./table.vue" +import GvaNotice from "./notice.vue" +import GvaQuickLink from "./quickLinks.vue" +import GvaWiki from "./wiki.vue" +import GvaPluginTable from "./pluginTable.vue" + +export { + GvaBanner, + GvaCard, + GvaChart, + GvaTable, + GvaNotice, + GvaQuickLink, + GvaWiki, + GvaPluginTable +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/notice.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/notice.vue new file mode 100644 index 000000000..e9c642757 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/notice.vue @@ -0,0 +1,78 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/pluginTable.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/pluginTable.vue new file mode 100644 index 000000000..8b02502d3 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/pluginTable.vue @@ -0,0 +1,63 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/quickLinks.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/quickLinks.vue new file mode 100644 index 000000000..8d9cd722c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/quickLinks.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/table.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/table.vue new file mode 100644 index 000000000..109b9e8cd --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/table.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/wiki.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/wiki.vue new file mode 100644 index 000000000..026604551 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/components/wiki.vue @@ -0,0 +1,42 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/index.vue new file mode 100644 index 000000000..7b44efe09 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/dashboard/index.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/error/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/error/index.vue new file mode 100644 index 000000000..1665acaeb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/error/index.vue @@ -0,0 +1,28 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/error/reload.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/error/reload.vue new file mode 100644 index 000000000..2504c55f0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/error/reload.vue @@ -0,0 +1,14 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/breakpoint/breakpoint.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/breakpoint/breakpoint.vue new file mode 100644 index 000000000..0751a471b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/breakpoint/breakpoint.vue @@ -0,0 +1,295 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/customer/customer.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/customer/customer.vue new file mode 100644 index 000000000..be3a25c9a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/customer/customer.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/index.vue new file mode 100644 index 000000000..8b322d621 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/index.vue @@ -0,0 +1,22 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/upload/upload.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/upload/upload.vue new file mode 100644 index 000000000..1b08a837d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/example/upload/upload.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/abnormalTransactions/abnormalTransactions.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/abnormalTransactions/abnormalTransactions.vue new file mode 100644 index 000000000..4488aa63d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/abnormalTransactions/abnormalTransactions.vue @@ -0,0 +1,443 @@ + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/abnormalTransactions/abnormalTransactionsForm.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/abnormalTransactions/abnormalTransactionsForm.vue new file mode 100644 index 000000000..a45ae07e0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/abnormalTransactions/abnormalTransactionsForm.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/index.vue new file mode 100644 index 000000000..514ef32fb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/index.vue @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/invoices/invoices.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/invoices/invoices.vue new file mode 100644 index 000000000..af8da0b9f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/invoices/invoices.vue @@ -0,0 +1,491 @@ + + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/invoices/invoicesForm.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/invoices/invoicesForm.vue new file mode 100644 index 000000000..c52f25f27 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/invoices/invoicesForm.vue @@ -0,0 +1,149 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/merchant/chart.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/merchant/chart.vue new file mode 100644 index 000000000..f2b208c96 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/merchant/chart.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/merchant/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/merchant/index.vue new file mode 100644 index 000000000..91109e95c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/merchant/index.vue @@ -0,0 +1,400 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/getTx.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/getTx.vue new file mode 100644 index 000000000..ccb20c87d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/getTx.vue @@ -0,0 +1,362 @@ + + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/keys.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/keys.vue new file mode 100644 index 000000000..f87622b3c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/keys.vue @@ -0,0 +1,447 @@ + + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/ops.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/ops.vue new file mode 100644 index 000000000..6198e337c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/regulator/ops.vue @@ -0,0 +1,143 @@ + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/chart.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/chart.vue new file mode 100644 index 000000000..6c778bb8d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/chart.vue @@ -0,0 +1,136 @@ + + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/encrypted.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/encrypted.vue new file mode 100644 index 000000000..cc6cc038a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/encrypted.vue @@ -0,0 +1,612 @@ + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/tx.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/tx.vue new file mode 100644 index 000000000..e6f4401ee --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/tx.vue @@ -0,0 +1,205 @@ + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/wallet.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/wallet.vue new file mode 100644 index 000000000..aa009c155 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/hufu/user/wallet.vue @@ -0,0 +1,466 @@ + + + + + \ No newline at end of file diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/init/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/init/index.vue new file mode 100644 index 000000000..c60473110 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/init/index.vue @@ -0,0 +1,395 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/asyncSubmenu.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/asyncSubmenu.vue new file mode 100644 index 000000000..678683fa5 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/asyncSubmenu.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/index.vue new file mode 100644 index 000000000..8558655ab --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/index.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/menuItem.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/menuItem.vue new file mode 100644 index 000000000..b20bbfb60 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/asideComponent/menuItem.vue @@ -0,0 +1,47 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/combinationMode.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/combinationMode.vue new file mode 100644 index 000000000..f8a934188 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/combinationMode.vue @@ -0,0 +1,138 @@ + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/headMode.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/headMode.vue new file mode 100644 index 000000000..bbadb0d4b --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/headMode.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/index.vue new file mode 100644 index 000000000..637208a41 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/normalMode.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/normalMode.vue new file mode 100644 index 000000000..64fefa5ea --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/aside/normalMode.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/header/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/header/index.vue new file mode 100644 index 000000000..a5d7276f8 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/header/index.vue @@ -0,0 +1,153 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/header/tools.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/header/tools.vue new file mode 100644 index 000000000..cfae5530f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/header/tools.vue @@ -0,0 +1,181 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/index.vue new file mode 100644 index 000000000..1f717d643 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/index.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/screenfull/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/screenfull/index.vue new file mode 100644 index 000000000..ab8da7f0a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/screenfull/index.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/search/search.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/search/search.vue new file mode 100644 index 000000000..eb9a25009 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/search/search.vue @@ -0,0 +1,104 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/setting/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/setting/index.vue new file mode 100644 index 000000000..e86237f7e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/setting/index.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/tabs/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/tabs/index.vue new file mode 100644 index 000000000..28d11a180 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/layout/tabs/index.vue @@ -0,0 +1,413 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/login/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/login/index.vue new file mode 100644 index 000000000..2108b0750 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/login/index.vue @@ -0,0 +1,379 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/person/person.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/person/person.vue new file mode 100644 index 000000000..d7d179498 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/person/person.vue @@ -0,0 +1,507 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/routerHolder.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/routerHolder.vue new file mode 100644 index 000000000..da4e5fb7c --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/routerHolder.vue @@ -0,0 +1,25 @@ + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/api/api.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/api/api.vue new file mode 100644 index 000000000..498bfcc3d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/api/api.vue @@ -0,0 +1,891 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/authority.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/authority.vue new file mode 100644 index 000000000..f40969ff0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/authority.vue @@ -0,0 +1,457 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/apis.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/apis.vue new file mode 100644 index 000000000..9815026ad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/apis.vue @@ -0,0 +1,173 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/datas.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/datas.vue new file mode 100644 index 000000000..b53a0f7ad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/datas.vue @@ -0,0 +1,144 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/menus.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/menus.vue new file mode 100644 index 000000000..973f75198 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/authority/components/menus.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/dictionary/sysDictionary.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/dictionary/sysDictionary.vue new file mode 100644 index 000000000..d73c6a323 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/dictionary/sysDictionary.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue new file mode 100644 index 000000000..712a97444 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue @@ -0,0 +1,362 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/index.vue new file mode 100644 index 000000000..d622809a7 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/index.vue @@ -0,0 +1,23 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/components/components-cascader.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/components/components-cascader.vue new file mode 100644 index 000000000..04b863d80 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/components/components-cascader.vue @@ -0,0 +1,124 @@ + + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/icon.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/icon.vue new file mode 100644 index 000000000..33a747007 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/icon.vue @@ -0,0 +1,1184 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/menu.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/menu.vue new file mode 100644 index 000000000..0c83be6ad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/menu/menu.vue @@ -0,0 +1,808 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/operation/sysOperationRecord.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/operation/sysOperationRecord.vue new file mode 100644 index 000000000..6bf2c9cfc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/operation/sysOperationRecord.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/params/sysParams.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/params/sysParams.vue new file mode 100644 index 000000000..5b1dfba3a --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/params/sysParams.vue @@ -0,0 +1,453 @@ + + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/user/user.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/user/user.vue new file mode 100644 index 000000000..e7c62ffbe --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/superAdmin/user/user.vue @@ -0,0 +1,559 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/system/state.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/system/state.vue new file mode 100644 index 000000000..4b428333d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/system/state.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/component/fieldDialog.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/component/fieldDialog.vue new file mode 100644 index 000000000..4a00b6890 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/component/fieldDialog.vue @@ -0,0 +1,492 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/component/previewCodeDialg.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/component/previewCodeDialg.vue new file mode 100644 index 000000000..6d78672bc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/component/previewCodeDialg.vue @@ -0,0 +1,92 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/index.vue new file mode 100644 index 000000000..62d952f74 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCode/index.vue @@ -0,0 +1,1454 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCodeAdmin/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCodeAdmin/index.vue new file mode 100644 index 000000000..b1561de62 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoCodeAdmin/index.vue @@ -0,0 +1,398 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoPkg/autoPkg.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoPkg/autoPkg.vue new file mode 100644 index 000000000..6a1f7efed --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/autoPkg/autoPkg.vue @@ -0,0 +1,254 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/exportTemplate/exportTemplate.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/exportTemplate/exportTemplate.vue new file mode 100644 index 000000000..a3cb1248f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/exportTemplate/exportTemplate.vue @@ -0,0 +1,963 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/formCreate/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/formCreate/index.vue new file mode 100644 index 000000000..75f6db8bf --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/formCreate/index.vue @@ -0,0 +1,20 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/index.vue new file mode 100644 index 000000000..e7cba00ad --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/index.vue @@ -0,0 +1,23 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/installPlugin/index.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/installPlugin/index.vue new file mode 100644 index 000000000..4aaa25efc --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/installPlugin/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/pubPlug/pubPlug.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/pubPlug/pubPlug.vue new file mode 100644 index 000000000..dcba57be6 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/pubPlug/pubPlug.vue @@ -0,0 +1,234 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/system/system.vue b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/system/system.vue new file mode 100644 index 000000000..83b370feb --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/src/view/systemTools/system/system.vue @@ -0,0 +1,823 @@ + + + + + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/tailwind.config.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/tailwind.config.js new file mode 100644 index 000000000..0130bdd1f --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/tailwind.config.js @@ -0,0 +1,30 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + important: true, + theme: { + extend: { + backgroundColor: { + "main": "#F5F5F5", + }, + textColor: { + "active": "var(--el-color-primary)", + }, + boxShadowColor: { + "active": "var(--el-color-primary)", + }, + borderColor: { + "table-border": "var(--el-border-color-lighter)", + } + }, + }, + darkMode: "class", + plugins: [], + corePlugins: { + preflight: false + } +} + diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vite.config.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vite.config.js new file mode 100644 index 000000000..6767844d0 --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vite.config.js @@ -0,0 +1,103 @@ +import legacyPlugin from '@vitejs/plugin-legacy' +import { viteLogo } from './src/core/config' +import Banner from 'vite-plugin-banner' +import * as path from 'path' +import * as dotenv from 'dotenv' +import * as fs from 'fs' +import vuePlugin from '@vitejs/plugin-vue' +import vueDevTools from 'vite-plugin-vue-devtools' +import VueFilePathPlugin from './vitePlugin/componentName/index.js' +import { svgBuilder } from 'vite-auto-import-svg' +import { AddSecret } from './vitePlugin/secret' +// @see https://cn.vitejs.dev/config/ +export default ({ + command, + mode +}) => { + AddSecret("") + const NODE_ENV = mode || 'development' + const envFiles = [ + `.env.${NODE_ENV}` + ] + for (const file of envFiles) { + const envConfig = dotenv.parse(fs.readFileSync(file)) + for (const k in envConfig) { + process.env[k] = envConfig[k] + } + } + + viteLogo(process.env) + + const timestamp = Date.parse(new Date()) + + const optimizeDeps = {} + + const alias = { + '@': path.resolve(__dirname, './src'), + 'vue$': 'vue/dist/vue.runtime.esm-bundler.js', + } + + const esbuild = {} + + const rollupOptions = { + output: { + entryFileNames: 'assets/087AC4D233B64EB0[name].[hash].js', + chunkFileNames: 'assets/087AC4D233B64EB0[name].[hash].js', + assetFileNames: 'assets/087AC4D233B64EB0[name].[hash].[ext]', + }, + } + + const config = { + base: '/', // 编译后js导入的资源路径 + root: './', // index.html文件所在位置 + publicDir: 'public', // 静态资源文件夹 + resolve: { + alias, + }, + define: { + 'process.env': {} + }, + server: { + // 如果使用docker-compose开发模式,设置为false + open: true, + port: process.env.VITE_CLI_PORT, + proxy: { + // 把key的路径代理到target位置 + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VITE_BASE_API]: { // 需要代理的路径 例如 '/api' + target: `${process.env.VITE_BASE_PATH}:${process.env.VITE_SERVER_PORT}/`, // 代理到 目标路径 + changeOrigin: true, + rewrite: path => path.replace(new RegExp('^' + process.env.VITE_BASE_API), ''), + } + }, + }, + build: { + minify: 'terser', // 是否进行压缩,boolean | 'terser' | 'esbuild',默认使用terser + manifest: false, // 是否产出manifest.json + sourcemap: false, // 是否产出sourcemap.json + outDir: 'dist', // 产出目录 + terserOptions: { + compress: { + //生产环境时移除console + drop_console: true, + drop_debugger: true, + }, + }, + rollupOptions, + }, + esbuild, + optimizeDeps, + plugins: [ + process.env.VITE_POSITION === 'open' && vueDevTools({launchEditor: process.env.VITE_EDITOR}), + legacyPlugin({ + targets: ['Android > 39', 'Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15'], + }), + vuePlugin(), + svgBuilder('./src/assets/icons/'), + svgBuilder('./src/plugin/'), + [Banner(`\n Build based on gin-vue-admin \n Time : ${timestamp}`)], + VueFilePathPlugin("./src/pathInfo.json") + ], + } + return config +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vitePlugin/componentName/index.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vitePlugin/componentName/index.js new file mode 100644 index 000000000..02bf1897e --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vitePlugin/componentName/index.js @@ -0,0 +1,73 @@ +import fs from 'fs'; +import path from 'path'; +import chokidar from 'chokidar'; + +// 递归获取目录下所有的 .vue 文件 +const getAllVueFiles = (dir, fileList = []) => { + const files = fs.readdirSync(dir); + files.forEach(file => { + const filePath = path.join(dir, file); + if (fs.statSync(filePath).isDirectory()) { + getAllVueFiles(filePath, fileList); + } else if (filePath.endsWith('.vue')) { + fileList.push(filePath); + } + }); + return fileList; +} + +// 从 .vue 文件内容中提取组件名称 +const extractComponentName = (fileContent) => { + const regex = /defineOptions\(\s*{\s*name:\s*["']([^"']+)["']/; + const match = fileContent.match(regex); + return match ? match[1] : null; +} + +// Vite 插件定义 +const vueFilePathPlugin = (outputFilePath) => { + let root; + + const generatePathNameMap = () => { + const vueFiles = [ + ...getAllVueFiles(path.join(root, 'src/view')), + ...getAllVueFiles(path.join(root, 'src/plugin')) + ]; + const pathNameMap = vueFiles.reduce((acc, filePath) => { + const content = fs.readFileSync(filePath, 'utf-8'); + const componentName = extractComponentName(content); + if (componentName) { + let relativePath ="/" + path.relative(root, filePath).replace(/\\/g, '/'); + acc[relativePath] = componentName; + } + return acc; + }, {}); + const outputContent = JSON.stringify(pathNameMap, null, 2); + fs.writeFileSync(outputFilePath, outputContent); + }; + + const watchDirectoryChanges = () => { + const watchDirectories = [path.join(root, 'src/view'), path.join(root, 'src/plugin')]; + const watcher = chokidar.watch(watchDirectories, { persistent: true, ignoreInitial: true }); + watcher.on('all', (event, path) => { + console.log(`File ${path} has been ${event}`); + generatePathNameMap(); + }); + }; + + + + return { + name: 'vue-file-path-plugin', + configResolved(resolvedConfig) { + root = resolvedConfig.root; + }, + buildEnd() { + generatePathNameMap(); + if (process.env.NODE_ENV === 'development') { + watchDirectoryChanges(); + } + }, + }; +} + +export default vueFilePathPlugin diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vitePlugin/secret/index.js b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vitePlugin/secret/index.js new file mode 100644 index 000000000..56fd3333d --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/Projects/web/vitePlugin/secret/index.js @@ -0,0 +1,6 @@ +export function AddSecret(secret) { + if(!secret){ + secret = "" + } + global['gva-secret'] = secret; +} diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/README.md b/2024-shenzhen-FinTechathon/Digital_Hufu/README.md new file mode 100644 index 000000000..881db3aec --- /dev/null +++ b/2024-shenzhen-FinTechathon/Digital_Hufu/README.md @@ -0,0 +1,154 @@ +# 数字虎符:央行数字货币场景下的隐私保护系统 + +## 项目背景 + +移动支付正在成为支付领域的新常态,但随着技术更迭,移动支付的也在面临着新的挑战: + +### 挑战1:对移动支付的隐私与安全担忧 + +数据显示,65.9%的用户在使用移动支付时担心存在安全隐患。在移动支付时遭遇安全问题的用户中,59.2%的用户遭遇个人信息泄露问题。为此,我们提出了以下构想: + +1. **用户与政府之间引入技术缓冲层**:通过混币代理和评审团机制,建立了一个技术性的“缓冲层”,在用户和政府之间增加了隐私保护的屏障 +2. **隐私保护机制**:交易信息经过加密、混币和脱敏处理,交易对象和第三方平台均无法直接获取用户交易信息,即使是监管机构也需要经过严格的流程和多方授权才能访问具体交易数据 +3. **透明监管流程**:利用区块链和智能合约记录监管机构的密钥申请和评审过程,增加了流程的透明度和可信度,能有效地防止滥用权力 +4. **平衡隐私与监管**:在保护用户正常交易隐私的同时,仍然满足了监管机构对异常交易的监控和必要的审计,这样的监管和可溯源性也是对用户交易的保护,能有效遏制电子诈骗、盗刷等问题 + +### 挑战2:移动支付平台(支付宝、微信支付)的市场竞争 + +数字人民币正面临这现有移动支付平台的竞争:从线上渗透率来看,我国线下场景移动支付用户使用产品渗透率前三的是支付宝、微信、云闪付;此外数字人民币渗透率提高最高,2023年数字人民币渗透率达到了11.9%。 + +调查数据显示,没有用过数字人民币但猜测其体验感和支付宝、微信支付差不多的被调查者几乎占一半。只有11.59%的被调查者使用过数字人民币,其中认为“支付宝、微信支付更好用”的人有40.98%。然而,超七成被调查者表示,在使用支付宝微信支付时有隐私方面的顾虑。 + +因此,隐私保护、小额钱款的追踪问题将成为本项目市场竞争优势的来源:我们将使用先进的隐私保护机制为用户提供更高的交易匿名性和隐私保护,同时,利用区块链的不可篡改特性结合数字人民币的法定货币特性,实现交易的追踪与溯源。这些功能是当前主流移动支付平台(如支付宝和微信支付)所缺乏的。 + +## 项目介绍 + +### 项目结构 + +我们的项目旨在通过混币服务和监管模块,在保护用户交易隐私的同时,满足监管机构的合规要求。核心思想是切断交易发送者与接收者之间的直接关联,防止交易被追踪,同时在必要时能够恢复交易链条,协助监管机构进行调查和审计。 + +具体的架构如下图所示:![](imgs\project_structure.png) + +### 核心模块 + +我们的项目主要包括以下两个核心模块: + +- **隐私保护模块**:通过混币支付代理服务,切断发送者与接收者之间的直接链接,防止外部实体追踪交易路径;同时运用密码学,密态存储交易信息,保障用户的交易隐私 +- **监管模块**:由混币代理初步判脱敏后的信息,对于可疑交易,将送至监管部门进一步审查。当检测到违规或可疑活动时,监管部门可调用智能合约向评审团请求相应的密钥,恢复交易链条,进行调查和审计。 + +### 项目流程 + +本项目的主要流程如下图所示,主要包含用户端、隐私保护端和监管端:![](imgs/project_pipeline.png) + +其中: + +隐私保护端主要包括**交易数据混洗+密态存储**: + +1. 交易加密发送:用户将交易信息加密后,发送给混币代理 + +2. 交易解密处理:混币代理在可信执行环境(TEE)中将交易解密,确保解密过程的安全性和数据的完整性 + +3. 交易混洗提交:混币代理将多个交易进行混洗处理,打乱交易的顺序和关联性,然后将混洗后的交易提交给数字人民币登记中心 + +4. 交易脱敏发送:混币代理将交易数据进行脱敏处理,去除敏感信息后,发送给监管智能体进行初步审核 + +5. 数据存储与密钥分发:混币代理将加密的交易数据存储在云平台上,并采用秘密共享方案,将密钥碎片分发给评审团成员(运行在区块链上) + +监管端主要包括**陪审团机制监管违规交易**: + +1. 违规检测: 监管智能体根据监管机构的要求,检测交易中是否存在违规或异常行为,无需访问用户的敏感信息 + +2. 异常情况报告: 当发现异常情况时,混币代理将涉嫌违规的账户信息发送给监管机构 + +3. 密钥申请流程: 监管机构通过调用智能合约,向评审团发起密钥申请,以获取解密嫌疑账户数据的权限 + +4. 评审团审核与密钥提供: 智能合约记录密钥申请请求,评审团成员独立评估申请的合法性。如果通过,他们将各自持有的密钥碎片发送给监管机构,同时在区块链上记录整个过程 + +5. 数据解密与调查: 监管机构收集到足够的密钥碎片后,合成完整的密钥,从云平台中调取并解密嫌疑账户的交易数据,进行深入调查和审计 + +### 项目技术 + +#### 混币技术 + +- **基本原理**:混币技术是一种通过将多个用户的加密货币交易混合,从而隐藏交易来源和去向的隐私保护技术。为了增强加密货币交易的不可链接性,通过将多个用户的资金混合在一起,模糊资金的来源和去向,提高交易的匿名性。这种服务可以有效切断发送方与接收方之间的联系,使得外部观察者难以追踪特定交易的发起者、接收者和金额。 + +- **常见的混币服务** + + - 中心化混币:需要第三方服务商提供混币服务,混币过程由第三方服务执行。常见的有Mixcoin、Blindcoin等。 + - 去中心化混币:利用用户约定的多方协议实现混币,解决了中心化混币第三方泄露隐私的风险。常见的有conjoin、coinshuffle、Tornado等。 + +- **技术应用**:本项目使用类似于tornado cash的混币服务(基于以太坊区块链的去中心化硬币混合服务),允许用户通过使用共享的资金池匿名化他们的交易。用户将加密货币存入智能合约中,智能合约将资金与其他用户的存款混合,从而打断资金源和接收地址之间的链接。 + + 以A向B转10CNY为例展示混币步骤:![](imgs/coin_shuffle.png) + + 1. 用户A向混币发送交易请求,交易内容为A向B转10CNY + + 2. 用户A将10CNY转给混币代理 + + 3. 混币代理随机将10CNY分成k份,转给B,其中每份金额随机 + +#### 秘密分享 + +- **基本原理**:秘密分享是一种密码学技术,将敏感信息(例如密钥或密码)分割成多个部分,分发给多个参与者,以确保只有某些预定数量的参与者能够重新组合并恢复秘密,而单独或不足数量的参与者无法获取任何有用的信息。 + +- **技术实现:基于拉格朗日插值的Shamir算法** + + Shamir秘密共享的核心是拉格朗日插值。对于给定的$k$个点$(x_1,y_1),(x_2,y_2),\cdots,(x_k,y_k)$,拉格朗日插值法能够计算出通过这些点的多项式。 + + 利用该方法给出的$k$阶多项式形式如下: + $ + L(x)=\sum_{i = 1}^{k} y_i l_i (x) + $ + + 为了让该多项式在$x = x_i$时取值为$y_i$,需要保证: + $ + l_i (x)=\begin{cases} + 1, & x = x_i\\ + 0, & x\neq x_i + \end{cases} + $ + + 因此可以构造$l_i (x)$如下: + $ + l_i (x)=\prod_{j = 1,j\neq i}^{k} \frac{(x - x_j)}{(x_i - x_j)} + $ + + 假设要共享的秘密为$M$,取$r - 1$个随机数$d_1,d_2,\cdots,d_{r - 1}$,构造一个$r - 1$次一元多项式: + $ + w(x)=M + d_1 x + d_2 x^2+\cdots + d_{r - 1} x^{r - 1} + $ + + 计算$D_1 = w(1),\cdots,D_i = w(i),\cdots,D_n = w(n)$,将计算的$n$个子秘密$(i,w_i)$分别分发给$n$个秘密共享的参与方。那么只要其中$k$方合作,即可唯一确定该拉格朗日多项式$L(x)$,并可以简单地求出$M = w(0)$。 + + 在实际应用中,该多项式可以被定义在素域$F_p$上,其中多项式的每一个系数是从素域中随机选取的非零且不等元素。 + + 假设一位攻击者拥有$r - 1$部分数据,并且: + $ + D = L(0)=\sum_{i = 1}^{r} D_i l_i (0),\text{ where } x_i = i + $ + + 因此: + $ + D_r l_r (0)=D-\sum_{i = 1}^{r - 1} D_i l_i (0),\text{ where } l_r (0)=\prod_{j = 1}^{r - 1} \frac{(0 - x_j)}{(r - x_j)} + $ + +- **技术应用** + - 对用户密钥进行分片,分发给评审团成员 + - 密钥碎片同样使用监管者的公钥加密保存 + - 监管机构收到预警后,向评审团发送证据,请求密钥碎片 + - 评审团对证据进行验证,验证成功后调用智能合约发送碎片 + - 监管机构对碎片解密,收到足够的数量的碎片后合成完整密钥 + - 监管机构使用完整密钥对加密信息解谜,获得完整的交易信息 + +#### 可信执行环境TEE + +- **基本原理**:(以下内容引自:[隐私计算技术解读:可信执行环境(TEE)概要及应用 - 知乎](https://zhuanlan.zhihu.com/p/480621560)) + + - **富执行环境(Rich Execution Environment,REE)** 指的是操作系统运行时的环境中,可以运行如 Android、IOS 等通用的 OS(Opreating System)。 + - **可信执行环境(Trusted Execution Environment,TEE)**是一种具有运算和储存功能,能提供安全性和完整性保护的独立处理环境。其基本思想是:在硬件中为敏感数据单独分配一块隔离的内存,所有敏感数据的计算均在这块内存中进行,并且除了经过授权的接口外,硬件中的其他部分不能访问这块隔离的内存中的信息。以此来实现敏感数据的隐私计算。 + - REE 是一个容易受到攻击的开放环境,如敏感数据的窃取、移动支付盗用等等。而 TEE 是中央处理器上的一个安全区域,能够保证敏感数据在隔离和可信的环境内被处理,从而免受来自 REE 中的软件攻击。 + - 此外,与其他的安全执行环境相比,TEE 可以端到端地保护 TA(Trusted Application)的完整性和机密性,能够提供更强的处理能力和更大的内存空间。在下图这一典型的可信执行环境架构中,TEE 内部为 REE 中的软件提供了接口,使得 REE 中的软件可以调用 TEE 对数据进行处理,但不会泄露敏感数据。 + + TEE与REE的模式如下图所示:![](imgs/TEE_REE.png) + +- **技术应用**:TEE主要用于为混币操作提供一个安全的操作和计算环境。在混币操作时,TEE 会利用硬件创建出一个隔离且安全的执行环境,交易数据的解密、混洗以及重新加密等步骤都在这个安全区域内完成,外部软件无法干涉和窃取。从而在硬件层面保障数据的保密性和完整性,进而有效保护交易隐私。![](imgs/TEE.png) diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/TEE.png b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/TEE.png new file mode 100644 index 000000000..29c238c48 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/TEE.png differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/TEE_REE.png b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/TEE_REE.png new file mode 100644 index 000000000..ffe74ffff Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/TEE_REE.png differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/coin_shuffle.png b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/coin_shuffle.png new file mode 100644 index 000000000..b1a940899 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/coin_shuffle.png differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/project_pipeline.png b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/project_pipeline.png new file mode 100644 index 000000000..1cc3d4f43 Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/project_pipeline.png differ diff --git a/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/project_structure.png b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/project_structure.png new file mode 100644 index 000000000..cf62ea94e Binary files /dev/null and b/2024-shenzhen-FinTechathon/Digital_Hufu/imgs/project_structure.png differ