├── ui ├── env.d.ts ├── src │ ├── hooks │ │ ├── echarts │ │ │ ├── README.md │ │ │ └── index.ts │ │ └── axios │ │ │ ├── README.md │ │ │ ├── help.ts │ │ │ └── index.ts │ ├── assets │ │ ├── logo.svg │ │ └── base.css │ ├── main.ts │ ├── stores │ │ └── counter.ts │ ├── api │ │ ├── code.ts │ │ ├── config.ts │ │ └── share.ts │ ├── components │ │ ├── icons │ │ │ ├── IconSupport.vue │ │ │ ├── IconTooling.vue │ │ │ ├── IconCommunity.vue │ │ │ ├── IconDocumentation.vue │ │ │ └── IconEcosystem.vue │ │ ├── Layout │ │ │ └── Index.vue │ │ └── Login.vue │ ├── App.vue │ ├── router │ │ └── index.ts │ ├── utils │ │ └── requests.ts │ └── views │ │ ├── HomeView.vue │ │ └── Edit.vue ├── .env.development ├── .env.production ├── public │ └── favicon.ico ├── .vscode │ └── extensions.json ├── tsconfig.config.json ├── tsconfig.json ├── index.html ├── .eslintrc.cjs ├── .gitignore ├── package.json ├── vite.config.ts ├── README.md ├── components.d.ts └── auto-import.d.ts ├── environment └── env.development ├── utils ├── cons.go ├── error │ └── error.go ├── result │ └── result.go └── util │ ├── jwt.go │ ├── util.go │ └── f2f.go ├── app.ini ├── static ├── img │ └── da428b3b6d6e058eb738e6b77a08e9b.jpg ├── html │ └── index.html └── tvshare.txt ├── .idea ├── vcs.xml ├── .gitignore ├── modules.xml ├── tvManager.iml └── dataSources.xml ├── model ├── config.go ├── main.go └── share.go ├── .gitignore ├── README.md ├── api ├── v1 │ ├── config.go │ └── share.go ├── wx │ └── weixin.go └── alipay │ └── f2f.go ├── main.go ├── router └── router.go ├── go.mod └── go.sum /ui/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /ui/src/hooks/echarts/README.md: -------------------------------------------------------------------------------- 1 | # useECharts 使用echarts -------------------------------------------------------------------------------- /ui/.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | 3 | VITE_PUBLIC_PATH = "./" -------------------------------------------------------------------------------- /ui/.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | VITE_PUBLIC_PATH = "./" -------------------------------------------------------------------------------- /ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biancangming/wtv-online/HEAD/ui/public/favicon.ico -------------------------------------------------------------------------------- /environment/env.development: -------------------------------------------------------------------------------- 1 | GIN_MODE=debug 2 | 3 | SERVER_PORT=8888 4 | 5 | SQLITE_PATH = "gorm.db" 6 | -------------------------------------------------------------------------------- /utils/cons.go: -------------------------------------------------------------------------------- 1 | package cons 2 | 3 | // 成功是0,失败是1 4 | const ( 5 | SuccessCode = iota 6 | FailCode 7 | ) 8 | -------------------------------------------------------------------------------- /app.ini: -------------------------------------------------------------------------------- 1 | [user] 2 | username = 3 | password = 4 | 5 | [config] 6 | title = 7 | qrcode_link = 8 | description = -------------------------------------------------------------------------------- /ui/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /static/img/da428b3b6d6e058eb738e6b77a08e9b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biancangming/wtv-online/HEAD/static/img/da428b3b6d6e058eb738e6b77a08e9b.jpg -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /utils/error/error.go: -------------------------------------------------------------------------------- 1 | package error 2 | 3 | type newErr struct { 4 | Err string 5 | } 6 | 7 | func (n newErr) Error() string { 8 | return n.Err 9 | } 10 | 11 | func NotFound(err string) error { 12 | return newErr{Err: err} 13 | } 14 | -------------------------------------------------------------------------------- /model/config.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Config struct { 4 | Username string `json:"username"` 5 | Password string `json:"password"` 6 | Title string 7 | QrcodeLink string 8 | Description string 9 | } 10 | 11 | var ConfigData Config -------------------------------------------------------------------------------- /ui/tsconfig.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.node.json", 3 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "types": ["node"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | users/index 8 | 9 | 10 | 你好 11 | 12 | -------------------------------------------------------------------------------- /ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import { createPinia } from "pinia"; 3 | 4 | import App from "./App.vue"; 5 | import router from "./router"; 6 | 7 | import "ant-design-vue/es/message/style/css"; 8 | 9 | const app = createApp(App); 10 | 11 | app.use(createPinia()); 12 | app.use(router); 13 | 14 | app.mount("#app"); 15 | -------------------------------------------------------------------------------- /.idea/tvManager.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.web.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["./src/*"] 8 | } 9 | }, 10 | 11 | "references": [ 12 | { 13 | "path": "./tsconfig.config.json" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /go_build_wtv_online.exe 2 | /gorm.db 3 | /wtv-online_linux_amd64 4 | ./ui/node_modules 5 | /wtv-online_darwin_amd64 6 | /wtv-online_darwin_arm64 7 | /wtv-online_linux_386 8 | /wtv-online_linux_arm 9 | /wtv-online_linux_arm64 10 | /wtv-online_linux_ppc64le 11 | /wtv-online_linux_s390x 12 | /wtv-online_windows_386.exe 13 | /wtv-online_windows_amd64.exe 14 | -------------------------------------------------------------------------------- /ui/src/stores/counter.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | 3 | export const useCounterStore = defineStore({ 4 | id: "counter", 5 | state: () => ({ 6 | counter: 0, 7 | }), 8 | getters: { 9 | doubleCount: (state) => state.counter * 2, 10 | }, 11 | actions: { 12 | increment() { 13 | this.counter++; 14 | }, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /model/main.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "github.com/glebarez/sqlite" 6 | "gorm.io/gorm" 7 | "os" 8 | ) 9 | 10 | var ( 11 | dbs *gorm.DB //SQLLITE 12 | ) 13 | 14 | func InitModel() { 15 | cwd, _ := os.Getwd() 16 | dbs, _ = gorm.Open(sqlite.Open(cwd+"/gorm.db"), &gorm.Config{}) 17 | err := dbs.AutoMigrate(&Share{}) 18 | fmt.Println(err) 19 | } 20 | -------------------------------------------------------------------------------- /ui/src/api/code.ts: -------------------------------------------------------------------------------- 1 | import { useDefRequest } from "@/utils/requests"; 2 | 3 | // 添加识别码 4 | export function useCodeAdd() { 5 | return useDefRequest({ 6 | url: "/api/code/add", 7 | method: "POST", 8 | }); 9 | } 10 | 11 | // 获取code 12 | export function useCodeGet() { 13 | return useDefRequest({ 14 | url: "/api/code/get", 15 | method: "GET", 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /ui/src/components/icons/IconSupport.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 超级文本链接分享工具 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ui/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require("@rushstack/eslint-patch/modern-module-resolution"); 3 | 4 | module.exports = { 5 | root: true, 6 | extends: [ 7 | "plugin:vue/vue3-essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript/recommended", 10 | "@vue/eslint-config-prettier", 11 | ], 12 | rules: { 13 | "vue/multi-word-component-names": 0, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /ui/src/hooks/axios/README.md: -------------------------------------------------------------------------------- 1 | # Vue Axios Composition Util 2 | 3 | `npm i axios` 4 | 5 | 1. 支持防抖 6 | 2. 拓展请求可取消 7 | 3. 错误自动捕获,数据自动响应 8 | 4. 拓展流文件下载插件 9 | 5. 级联请求(未开发、计划) 10 | 11 | ## 创建一个axios 实例 12 | 13 | `createAxios` 14 | 15 | ## 返回参数 16 | 17 | `server` 18 | `useAxiosRequest` 19 | 20 | ### 请求hook使用 `useAxiosRequest` 21 | ```js 22 | /** 23 | * @param {AxiosRequestConfig} config 24 | * @param {HowVesExRequestOptions} options? 额外选项,立即执行, 防抖延迟时间 25 | * @returns 26 | */ 27 | ``` 28 | -------------------------------------------------------------------------------- /ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /ui/src/api/config.ts: -------------------------------------------------------------------------------- 1 | import { useAxiosRequest } from "@/utils/requests"; 2 | 3 | export function useIsRequiredLogin() { 4 | return useAxiosRequest( 5 | { 6 | url: "/api/config/isRequiredLogin", 7 | }, 8 | { 9 | immediate: true, 10 | } 11 | ); 12 | } 13 | 14 | export function useConfig() { 15 | return useAxiosRequest<{ 16 | title: string; 17 | qrcodeLink: string; 18 | description: string; 19 | }>( 20 | { 21 | url: "/api/config/config", 22 | }, 23 | { 24 | immediate: true, 25 | defaultVal: {}, 26 | } 27 | ); 28 | } 29 | 30 | export function UseLogin() { 31 | return useAxiosRequest({ 32 | url: "/api/config/login", 33 | method: "POST", 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /ui/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from "vue-router"; 2 | import HomeView from "../views/HomeView.vue"; 3 | import EditView from "../views/Edit.vue"; 4 | import Layout from "@/components/Layout/Index.vue"; 5 | const router = createRouter({ 6 | history: createWebHashHistory(import.meta.env.BASE_URL), 7 | routes: [ 8 | { 9 | path: "/", 10 | component: Layout, 11 | redirect: { 12 | name: "home", 13 | }, 14 | children: [ 15 | { 16 | path: "/home", 17 | name: "home", 18 | component: HomeView, 19 | }, 20 | { 21 | path: "/edit", 22 | name: "edit", 23 | component: EditView, 24 | }, 25 | ], 26 | }, 27 | ], 28 | }); 29 | 30 | export default router; 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | 使用本程序可在线托管txt、m3u、m3u8、yml、json等文本,可用于iptv电视连接配置,geojson地图数据保存,私人程序固定数据配置等。 4 | 5 | # 部署教程 6 | 7 | https://mp.weixin.qq.com/s/pK-8EbJ_4s3C8FsFgXSb3w 8 | 9 | # 演示网站 10 | 11 | 12 | http://online.frpsss.tk/#/home 13 | 14 | 本站只用作演示、请勿用于个人真实生产,数据可能随时被删除 15 | 16 | # 设置登录项,网站页面文字配置 17 | 在文件根目录下设置,`app.ini`配置如下 18 | ```ini 19 | [user] 20 | username =1 21 | password = 1 22 | 23 | [config] 24 | title = 25 | qrcode_link = 26 | description = 27 | ``` 28 | 29 | # 打包教程 30 | 安装 31 | ```shell 32 | go get github.com/jteeuwen/go-bindata/... 33 | go get github.com/elazarl/go-bindata-assetfs/... 34 | ``` 35 | 36 | 执行 `go-bindata-assetfs .\ui\dist` 生成 `bindata.go` 文件,并复制到router目录下 37 | 38 | 打包命令,全局需要安装`gox` 39 | 40 | - 编译linux gox -os="linux" 41 | - 编译windows gox -os="windows" 42 | - 编译darwin gox -os="darwin" 43 | -------------------------------------------------------------------------------- /ui/src/api/share.ts: -------------------------------------------------------------------------------- 1 | import { useDefRequest } from "@/utils/requests"; 2 | 3 | export function useShareUpdate() { 4 | return useDefRequest({ 5 | url: "/api/share/update", 6 | method: "POST", 7 | }); 8 | } 9 | 10 | export function useShareUpdateStatus() { 11 | return useDefRequest({ 12 | url: "/api/share/updateStatus", 13 | method: "POST", 14 | }); 15 | } 16 | 17 | export function useShareGet() { 18 | return useDefRequest({ 19 | url: "/api/share/get", 20 | method: "GET", 21 | }); 22 | } 23 | 24 | // useStatus 使用状态 0 启用中 1 是废弃 25 | export function useShareUrls(immediate = false, useStatus: 0 | 1) { 26 | return useDefRequest( 27 | { 28 | url: "/api/share/urls", 29 | params: { 30 | useStatus, 31 | }, 32 | }, 33 | { 34 | immediate, 35 | defaultVal: [], 36 | } 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /api/v1/config.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "wtv-online/model" 7 | ) 8 | 9 | func IsRequiredLogin(c *gin.Context) { 10 | config := model.ConfigData 11 | if config.Username == "" && config.Password == "" { 12 | c.String(http.StatusOK, "0") 13 | } else { 14 | c.String(http.StatusOK, "1") 15 | } 16 | } 17 | 18 | func GetConfigData(c *gin.Context) { 19 | config := model.ConfigData 20 | c.JSON(http.StatusOK, gin.H{ 21 | "title": config.Title, 22 | "qrcodeLink": config.QrcodeLink, 23 | "description": config.Description, 24 | }) 25 | } 26 | 27 | func Login(c *gin.Context) { 28 | config := model.Config{} 29 | _ = c.ShouldBindJSON(&config) 30 | if config.Username == model.ConfigData.Username && config.Password == model.ConfigData.Password { 31 | c.String(http.StatusOK, "1") 32 | } else { 33 | c.String(http.StatusOK, "0") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /utils/result/result.go: -------------------------------------------------------------------------------- 1 | package result 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | cons "wtv-online/utils" 6 | ) 7 | 8 | func Ok(data interface{}, msg string) gin.H { 9 | return gin.H{ 10 | "code": cons.SuccessCode, 11 | "data": data, 12 | "msg": msg, 13 | } 14 | } 15 | 16 | func Fail(msg string) gin.H { 17 | return gin.H{ 18 | "code": cons.FailCode, 19 | "data": nil, 20 | "msg": msg, 21 | } 22 | } 23 | 24 | func PageOk(records interface{}, total int64, current, size int, msg string) gin.H { 25 | return gin.H{ 26 | "code": cons.SuccessCode, 27 | "data": gin.H{ 28 | "total": total, 29 | "records": records, 30 | "current": current, 31 | "size": size, 32 | }, 33 | "msg": msg, 34 | } 35 | } 36 | 37 | func PageFail(msg string) gin.H { 38 | return gin.H{ 39 | "code": cons.FailCode, 40 | "data": gin.H{ 41 | "total": 0, 42 | "records": nil, 43 | "current": 0, 44 | "size": 0, 45 | }, 46 | "msg": msg, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ui/src/components/icons/IconTooling.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /ui/src/components/icons/IconCommunity.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /ui/src/components/icons/IconDocumentation.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sqlite.xerial 6 | true 7 | org.sqlite.JDBC 8 | jdbc:sqlite:D:\mycode\wtv-online-local\gorm.db 9 | $ProjectFileDir$ 10 | 11 | 12 | sqlite.xerial 13 | true 14 | org.sqlite.JDBC 15 | jdbc:sqlite:identifier.sqlite 16 | $ProjectFileDir$ 17 | 18 | 19 | sqlite.xerial 20 | true 21 | org.sqlite.JDBC 22 | jdbc:sqlite:identifier.sqlite 23 | $ProjectFileDir$ 24 | 25 | 26 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-ini/ini" 6 | ) 7 | 8 | import ( 9 | "github.com/gin-gonic/gin" 10 | "os" 11 | "wtv-online/model" 12 | "wtv-online/router" 13 | ) 14 | 15 | func InitAppIni() { 16 | Cfg, err := ini.Load("app.ini") 17 | if err != nil { 18 | fmt.Println("未设置配置文件,跳过此步骤") 19 | return 20 | } 21 | userSection := Cfg.Section("user") 22 | configSection := Cfg.Section("config") 23 | username := userSection.Key("username").MustString("") 24 | password := userSection.Key("password").MustString("") 25 | 26 | title := configSection.Key("title").MustString("超级文本链接分享工具 - 公众号 一个橙子pro") 27 | qrcodeLink := configSection.Key("qrcode_link").MustString("http://online.bianbingdang.com/da428b3b6d6e058eb738e6b77a08e9b.jpg") 28 | description := configSection.Key("description").MustString("一个橙子出品, 上边是我的微信公众号,本网站支持私有部署,https://github.com/biancangming/wtv-online 。本网站是本人纯手工制作,请勿无脑攻击,请勿使用本站发布皇都读、政治敏感、道德沦丧等信息,本人一经发现立即删除。") 29 | 30 | model.ConfigData = model.Config{ 31 | Username: username, 32 | Password: password, 33 | Title: title, 34 | QrcodeLink: qrcodeLink, 35 | Description: description, 36 | } 37 | fmt.Println(model.ConfigData) 38 | } 39 | 40 | func main() { 41 | //编译 gox -os="linux windows darwin" 42 | gin.SetMode(os.Getenv(gin.ReleaseMode)) 43 | InitAppIni() 44 | model.InitModel() 45 | router.InitRouter() 46 | } 47 | -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "run-p type-check build-only", 7 | "preview": "vite preview --port 4173", 8 | "build-only": "vite build", 9 | "type-check": "vue-tsc --noEmit", 10 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" 11 | }, 12 | "dependencies": { 13 | "@vueuse/core": "^8.7.4", 14 | "@vueuse/integrations": "^8.7.5", 15 | "ant-design-vue": "^3.2.7", 16 | "axios": "^0.27.2", 17 | "echarts": "^5.3.3", 18 | "howtools": "^0.2.2", 19 | "pinia": "^2.0.14", 20 | "universal-cookie": "^4.0.4", 21 | "vue": "^3.2.37", 22 | "vue-router": "^4.0.16" 23 | }, 24 | "devDependencies": { 25 | "@rushstack/eslint-patch": "^1.1.0", 26 | "@types/echarts": "^4.9.15", 27 | "@types/node": "^16.11.41", 28 | "@vitejs/plugin-vue": "^2.3.3", 29 | "@vitejs/plugin-vue-jsx": "^1.3.10", 30 | "@vue/eslint-config-prettier": "^7.0.0", 31 | "@vue/eslint-config-typescript": "^11.0.0", 32 | "@vue/tsconfig": "^0.1.3", 33 | "eslint": "^8.5.0", 34 | "eslint-plugin-vue": "^9.0.0", 35 | "less": "^4.1.3", 36 | "npm-run-all": "^4.1.5", 37 | "prettier": "^2.5.1", 38 | "typescript": "~4.7.4", 39 | "unplugin-auto-import": "^0.8.8", 40 | "unplugin-vue-components": "^0.19.6", 41 | "vite": "^2.9.12", 42 | "vue-tsc": "^0.38.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ui/src/components/Layout/Index.vue: -------------------------------------------------------------------------------- 1 | 28 | 33 | 55 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | "strings" 8 | v1 "wtv-online/api/v1" 9 | ) 10 | 11 | func getAssetContent(name string) string { 12 | b, _ := Asset(name) 13 | return string(b) 14 | } 15 | 16 | func InitRouter() { 17 | 18 | r := gin.New() 19 | fmt.Println() 20 | 21 | r.NoRoute(func(c *gin.Context) { 22 | rp := c.Request.URL.Path 23 | 24 | if rp == "/" { 25 | c.Writer.Header().Add("Content-Type", "text/html; charset=utf-8") 26 | c.String(http.StatusOK, getAssetContent("ui/dist/index.html")) 27 | return 28 | } 29 | 30 | if strings.HasSuffix(rp, ".js") { 31 | c.Writer.Header().Add("Content-Type", "application/javascript") 32 | } else if strings.HasSuffix(rp, ".css") { 33 | c.Writer.Header().Add("Content-Type", "text/css") 34 | } 35 | 36 | c.String(http.StatusOK, getAssetContent("ui/dist"+rp)) 37 | }) 38 | 39 | r.Static("/static", "./static") 40 | 41 | { 42 | v1g := r.Group("/api") 43 | 44 | // 链接分享 45 | { 46 | g := v1g.Group("/share") 47 | g.POST("update", v1.UpdateOrAddShare) 48 | g.POST("updateStatus", v1.UpdateUseStatus) 49 | g.GET("/:uuid", v1.GetShareUrl) 50 | g.GET("/get", v1.GetShareUrlData) 51 | g.GET("/urls", v1.GetShareUrls) 52 | } 53 | 54 | // 配置 55 | { 56 | g := v1g.Group("/config") 57 | g.GET("isRequiredLogin", v1.IsRequiredLogin) 58 | g.GET("config", v1.GetConfigData) 59 | g.POST("login", v1.Login) 60 | } 61 | 62 | } 63 | 64 | r.Run(":1999") 65 | } 66 | -------------------------------------------------------------------------------- /ui/src/utils/requests.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createAxios, 3 | type HowAxiosRequestConfig, 4 | type HowVesExRequestOptions, 5 | } from "@/hooks/axios"; 6 | import { message } from "ant-design-vue"; 7 | import { watchEffect, type Ref } from "vue"; 8 | import { watch } from "vue"; 9 | 10 | export const { useAxiosRequest } = createAxios({ 11 | instanceConfig: { 12 | baseURL: import.meta.env.VITE_LIVE_BASE as string, 13 | }, 14 | requestInterceptor: (config) => { 15 | if (config.headers) { 16 | config.headers["Authorization"] = sessionStorage.getItem("token") || ""; 17 | } 18 | return config; 19 | }, 20 | errResponseInterceptor: (err) => { 21 | if (err.request.status == 401) { 22 | location.href = "/#/login"; 23 | } 24 | }, 25 | }); 26 | 27 | export interface Result { 28 | code: 0 | 1; 29 | msg: string; 30 | data: T; 31 | } 32 | 33 | // 默认请求列表 34 | export function useDefRequest( 35 | config: HowAxiosRequestConfig, 36 | options?: HowVesExRequestOptions 37 | ) { 38 | return useAxiosRequest>(config, options); 39 | } 40 | 41 | // 错误提示辅助函数 42 | export function useDataTip(result: Ref> | any, error?: Ref) { 43 | watch(result, () => { 44 | if (result.value.code == 0) { 45 | message.success(result.value.msg); 46 | } else { 47 | message.error(result.value.msg); 48 | } 49 | }); 50 | 51 | if (error) { 52 | const msg = error.value?.response?.data.msg; 53 | watchEffect(() => msg && message.error(msg)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ui/src/hooks/echarts/index.ts: -------------------------------------------------------------------------------- 1 | import * as echarts from "echarts"; 2 | 3 | import type { ECharts } from "echarts"; 4 | import { onMounted, nextTick, type Ref, onUnmounted } from "vue"; 5 | import { addResizeListener, removeResizeListener } from "howtools"; 6 | 7 | export default function useECharts( 8 | el: Ref, 9 | theme: "light" | "dark" | "default" = "light" 10 | ) { 11 | //echarts图实例 12 | let echartInstance: ECharts | null = null; 13 | 14 | //设置默认样式数据 15 | const defaultOption: echarts.EChartsOption = { 16 | backgroundColor: theme == "dark" ? "rgba(0,0,0,0)" : "rgba(255,555,255)", 17 | }; 18 | 19 | function addDefaultOption(option: echarts.EChartsOption) { 20 | Object.assign(defaultOption, option); 21 | } 22 | 23 | async function setOption(option: echarts.EChartsOption) { 24 | if (!el.value) { 25 | await nextTick(); 26 | echartInstance = echarts.init(el.value, theme); 27 | } 28 | if (!echartInstance) throw new Error("echarts 实例没有创建成功"); 29 | 30 | echartInstance?.setOption(Object.assign(defaultOption, option), true); 31 | } 32 | 33 | function resize() { 34 | echartInstance?.resize(); 35 | } 36 | 37 | //初始化echarts图 38 | onMounted(() => { 39 | if (!el.value) return; 40 | echartInstance = echarts.init(el.value, theme); 41 | addResizeListener(el.value, resize); 42 | }); 43 | 44 | onUnmounted(() => { 45 | removeResizeListener(el.value, resize); 46 | }); 47 | 48 | return { 49 | addDefaultOption, 50 | setOption, 51 | resize, 52 | echartInstance, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /utils/util/jwt.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "github.com/dgrijalva/jwt-go" 7 | "time" 8 | error2 "wtv-online/utils/error" 9 | ) 10 | 11 | var jwtSecret = []byte("bcm-9c0b56e0-0f8a-43ab-856b-9bc5f37e2e7e") 12 | var TokenMap = map[string]string{} // 后端临时存储token位置 13 | 14 | type Claims struct { 15 | Username string `json:"username"` 16 | UserId int `json:"user_id"` 17 | jwt.StandardClaims 18 | } 19 | 20 | func GeneratePassword(s, salt string) string { 21 | b := []byte(s) 22 | h := md5.New() 23 | h.Write(b) 24 | h.Write([]byte(salt)) 25 | return hex.EncodeToString(h.Sum(nil)) 26 | } 27 | 28 | func GenerateToken(username string, userId int) (string, error) { 29 | nowTime := time.Now() //当前时间 30 | expireTime := nowTime.Add(24 * time.Hour) //有效时间 31 | 32 | claims := Claims{ 33 | Username: username, 34 | UserId: userId, 35 | StandardClaims: jwt.StandardClaims{ 36 | ExpiresAt: expireTime.Unix(), 37 | Issuer: "YiGeChengZi", 38 | }, 39 | } 40 | 41 | tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 42 | token, err := tokenClaims.SignedString(jwtSecret) 43 | m := Md5(username) 44 | TokenMap[m] = token 45 | return m, err 46 | } 47 | 48 | func ParseToken(token string) (*Claims, error) { 49 | tokenClaims, _ := jwt.ParseWithClaims(TokenMap[token], &Claims{}, func(token *jwt.Token) (interface{}, error) { 50 | return jwtSecret, nil 51 | }) 52 | 53 | if tokenClaims != nil { 54 | if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { 55 | return claims, nil 56 | } 57 | } 58 | return nil, error2.NotFound("登录已经失效") 59 | } 60 | -------------------------------------------------------------------------------- /ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from "url"; 2 | import Components from "unplugin-vue-components/vite"; 3 | import AutoImport from "unplugin-auto-import/vite"; 4 | import { AntDesignVueResolver } from "unplugin-vue-components/resolvers"; 5 | import type { ConfigEnv, UserConfigExport } from "vite"; 6 | import { loadEnv } from "vite"; 7 | import vue from "@vitejs/plugin-vue"; 8 | import vueJsx from "@vitejs/plugin-vue-jsx"; 9 | 10 | // https://vitejs.dev/config/ 11 | export default ({ mode }: ConfigEnv): UserConfigExport => { 12 | const root = __dirname; 13 | const env = loadEnv(mode, root); 14 | 15 | return { 16 | root, 17 | base: env.VITE_PUBLIC_PATH, 18 | build: { 19 | emptyOutDir: true, 20 | assetsInlineLimit: 1024 * 128, 21 | assetsDir: "", 22 | }, 23 | plugins: [ 24 | vue(), 25 | vueJsx(), 26 | Components({ 27 | resolvers: [AntDesignVueResolver()], 28 | }), 29 | AutoImport({ 30 | imports: ["vue"], 31 | dts: "./auto-import.d.ts", 32 | }), 33 | ], 34 | resolve: { 35 | alias: { 36 | "@": fileURLToPath(new URL("./src", import.meta.url)), 37 | }, 38 | }, 39 | server: { 40 | proxy: { 41 | "/api": { 42 | target: "http://127.0.0.1:1999/api/", 43 | ws: true, 44 | changeOrigin: true, 45 | rewrite: (path) => path.replace(/^\/api/, ""), 46 | }, 47 | "/static": { 48 | target: "http://127.0.0.1:1999/", 49 | ws: true, 50 | changeOrigin: true, 51 | rewrite: (path) => path.replace(/^\/api/, ""), 52 | }, 53 | }, 54 | }, 55 | }; 56 | }; 57 | -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | # ui 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 8 | 9 | ## Type Support for `.vue` Imports in TS 10 | 11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. 12 | 13 | If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: 14 | 15 | 1. Disable the built-in TypeScript Extension 16 | 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette 17 | 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` 18 | 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. 19 | 20 | ## Customize configuration 21 | 22 | See [Vite Configuration Reference](https://vitejs.dev/config/). 23 | 24 | ## Project Setup 25 | 26 | ```sh 27 | npm install 28 | ``` 29 | 30 | ### Compile and Hot-Reload for Development 31 | 32 | ```sh 33 | npm run dev 34 | ``` 35 | 36 | ### Type-Check, Compile and Minify for Production 37 | 38 | ```sh 39 | npm run build 40 | ``` 41 | 42 | ### Lint with [ESLint](https://eslint.org/) 43 | 44 | ```sh 45 | npm run lint 46 | ``` 47 | -------------------------------------------------------------------------------- /model/share.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/duke-git/lancet/v2/random" 5 | ) 6 | 7 | type Share struct { 8 | Uuid string `json:"uuid" gorm:"type:varchar(100);primaryKey"` 9 | Content string `json:"content" gorm:"type:text"` // 填入的内容 10 | FileType string `json:"fileType" gorm:"type:text"` 11 | Mark string `json:"mark" gorm:"type:text"` // 备注 12 | UseStatus int `json:"useStatus" gorm:"type:text"` 13 | } 14 | 15 | func GetShareUrl(uuid string) (Share, error) { 16 | s := Share{} 17 | var err error 18 | err = dbs.Debug().Model(&Share{}).Select([]string{"content", "uuid", "use_status", "file_type", "mark"}).Find(&s, "uuid = ?", uuid).Limit(1).Error 19 | 20 | if err != nil { 21 | return s, err 22 | } 23 | return s, nil 24 | } 25 | 26 | func GetShareUrls(us int) ([]Share, error) { 27 | s := new([]Share) 28 | if err := dbs.Model(&Share{}). 29 | Select([]string{"uuid", "file_type", "mark"}). 30 | Find(&s, "use_status = ?", us).Error; err != nil { 31 | return *s, err 32 | } 33 | return *s, nil 34 | } 35 | 36 | func UpdateOrAddShare(s Share) (Share, error) { 37 | var err error 38 | 39 | if s.Uuid == "" { 40 | s.Uuid, _ = random.UUIdV4() 41 | s.UseStatus = 0 42 | // 存储内容到sqlite 43 | if err := dbs.Model(&Share{}).Create(s).Error; err != nil { 44 | return s, err 45 | } 46 | return s, nil 47 | } else { 48 | // 更新数据到sqlite 49 | if err := dbs.Model(&Share{}).Where("uuid = ?", s.Uuid).Updates(s).Error; err != nil { 50 | return s, err 51 | } 52 | } 53 | 54 | if err != nil { 55 | return s, err 56 | } 57 | 58 | return s, nil 59 | } 60 | 61 | // UpdateUseStatus 修改使用状态 62 | func UpdateUseStatus(s Share) (Share, error) { 63 | table := dbs.Model(&Share{}) 64 | if err := table.Where("uuid = ?", s.Uuid).Update("use_status", s.UseStatus).Error; err != nil { 65 | return s, err 66 | } 67 | return s, nil 68 | } 69 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module wtv-online 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 7 | github.com/duke-git/lancet/v2 v2.0.9 8 | github.com/elazarl/go-bindata-assetfs v1.0.1 9 | github.com/gin-gonic/gin v1.7.7 10 | github.com/glebarez/sqlite v1.4.5 11 | github.com/go-ini/ini v1.67.0 12 | github.com/go-pay/gopay v1.5.79 13 | gorm.io/gorm v1.23.5 14 | ) 15 | 16 | require ( 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/glebarez/go-sqlite v1.17.2 // indirect 19 | github.com/go-playground/locales v0.13.0 // indirect 20 | github.com/go-playground/universal-translator v0.17.0 // indirect 21 | github.com/go-playground/validator/v10 v10.4.1 // indirect 22 | github.com/golang/protobuf v1.5.2 // indirect 23 | github.com/google/go-cmp v0.5.6 // indirect 24 | github.com/google/uuid v1.3.0 // indirect 25 | github.com/jinzhu/inflection v1.0.0 // indirect 26 | github.com/jinzhu/now v1.1.5 // indirect 27 | github.com/json-iterator/go v1.1.9 // indirect 28 | github.com/kr/pretty v0.1.0 // indirect 29 | github.com/leodido/go-urn v1.2.0 // indirect 30 | github.com/mattn/go-isatty v0.0.14 // indirect 31 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect 32 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect 33 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect 34 | github.com/ugorji/go/codec v1.1.7 // indirect 35 | golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect 36 | golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect 37 | google.golang.org/protobuf v1.26.0 // indirect 38 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 39 | gopkg.in/yaml.v2 v2.4.0 // indirect 40 | modernc.org/libc v1.16.8 // indirect 41 | modernc.org/mathutil v1.4.1 // indirect 42 | modernc.org/memory v1.1.1 // indirect 43 | modernc.org/sqlite v1.17.2 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /api/wx/weixin.go: -------------------------------------------------------------------------------- 1 | package wx 2 | 3 | import ( 4 | "encoding/xml" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "log" 8 | "net/http" 9 | "time" 10 | "wtv-online/utils/util" 11 | ) 12 | 13 | // WeiXinTextMsg 微信文本消息结构体 14 | type WeiXinTextMsg struct { 15 | ToUserName string 16 | FromUserName string 17 | CreateTime int64 18 | MsgType string 19 | Content string 20 | MsgId int64 21 | XMLName xml.Name `xml:"xml"` 22 | } 23 | 24 | // WeiXinReceive 包含id name 的下拉框 25 | func WeiXinReceive(c *gin.Context) { 26 | signature := c.Query("signature") 27 | timestamp := c.Query("timestamp") 28 | nonce := c.Query("nonce") 29 | echostr := c.Query("echostr") 30 | 31 | ok := util.CheckSignature(signature, timestamp, nonce, "yigechengzi") 32 | if !ok { 33 | log.Println("微信公众号接入校验失败!") 34 | c.String(http.StatusInternalServerError, "") 35 | return 36 | } 37 | 38 | log.Println("微信公众号接入校验成功!") 39 | c.String(http.StatusOK, echostr) 40 | } 41 | 42 | // WeiXinMsgReceive 微信消息接收 43 | func WeiXinMsgReceive(c *gin.Context) { 44 | var textMsg WeiXinTextMsg 45 | err := c.ShouldBindXML(&textMsg) 46 | if err != nil { 47 | log.Printf("[消息接收] - XML数据包解析失败: %v\n", err) 48 | return 49 | } 50 | 51 | log.Printf("[消息接收] - 收到消息, 消息类型为: %s, 消息内容为: %s\n", textMsg.MsgType, textMsg.Content) 52 | 53 | WXMsgReply(c, textMsg.ToUserName, textMsg.FromUserName) 54 | } 55 | 56 | // WXMsgReply 微信消息回复 57 | func WXMsgReply(c *gin.Context, fromUser, toUser string) { 58 | repTextMsg := WeiXinTextMsg{ 59 | ToUserName: toUser, 60 | FromUserName: fromUser, 61 | CreateTime: time.Now().Unix(), 62 | MsgType: "text", 63 | Content: fmt.Sprintf("[消息回复] - %s", time.Now().Format("2006-01-02 15:04:05")), 64 | } 65 | 66 | msg, err := xml.Marshal(&repTextMsg) 67 | if err != nil { 68 | log.Printf("[消息回复] - 将对象进行XML编码出错: %v\n", err) 69 | return 70 | } 71 | fmt.Println(string(msg)) 72 | //c.XML(http.StatusOK, msg) 73 | _, _ = c.Writer.Write(msg) 74 | } 75 | -------------------------------------------------------------------------------- /ui/src/hooks/axios/help.ts: -------------------------------------------------------------------------------- 1 | import { isRef, shallowRef, watch, type Ref } from "vue"; 2 | import type { AxiosResponse } from "axios"; 3 | import { saveFileFromBlob } from "howtools"; 4 | 5 | declare type contentTypeStr = 6 | | "application/*" 7 | | "application/msword" 8 | | "application/vnd.ms-excel" 9 | | "application/pdf" 10 | | "application/vnd.ms-powerpoint"; 11 | 12 | interface HowVesAxiosDownloadOptions { 13 | fileName?: string; 14 | contentType?: contentTypeStr; 15 | cbdata?: (res: AxiosResponse) => any; 16 | response?: Ref; 17 | } 18 | /** 19 | * @param {Ref>|any} data 需要下载的blob数据 20 | * @param {HowVesAxiosDownloadOptions} options? 21 | */ 22 | export function useFileDownLoad(options?: HowVesAxiosDownloadOptions) { 23 | const finished = shallowRef(false); //下载完成标志 24 | 25 | const { fileName, contentType, response, cbdata } = options || {}; 26 | 27 | const filenameReg = new RegExp("filename=([^;]+\\.[^\\.;]+);*"); 28 | 29 | const download = (_response: AxiosResponse) => { 30 | finished.value = false; 31 | 32 | //读取响应头 33 | const headers = _response.headers || {}; 34 | 35 | //读取文件类型 36 | const _contentType = contentType ?? headers["content-type"]; //读取文件类型 37 | if (!_contentType) throw new Error("contentType Cannot be empty"); 38 | 39 | // 读取文件名称 40 | const dispositionRegArr = filenameReg.exec( 41 | _response.headers["content-disposition"] 42 | ); 43 | const _fileName = 44 | fileName ?? decodeURI(dispositionRegArr ? dispositionRegArr[0] : ""); //读取文件类型 45 | if (!_fileName) throw new Error("fileName Cannot be empty"); 46 | 47 | //下载数据 48 | const data = cbdata ? cbdata(_response) : _response.data; 49 | saveFileFromBlob(data, _fileName, _contentType); 50 | finished.value = true; 51 | }; 52 | 53 | // 响应式则自动下载,非响应需要手动调用下载操作 54 | isRef(response) && watch(response, download); 55 | 56 | return { 57 | finished, 58 | download, 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /ui/src/components/icons/IconEcosystem.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /ui/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 34 | 77 | -------------------------------------------------------------------------------- /api/v1/share.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/go-pay/gopay/pkg/util" 6 | "net/http" 7 | "strings" 8 | "wtv-online/model" 9 | "wtv-online/utils/result" 10 | ) 11 | 12 | // GetShareUrl 获取单条信息内容 13 | func GetShareUrl(c *gin.Context) { 14 | up := c.Param("uuid") 15 | 16 | var r = strings.Split(up, ".") 17 | uuid := r[0] 18 | end := r[1] 19 | s, err := model.GetShareUrl(uuid) 20 | 21 | if err != nil { 22 | c.String(http.StatusOK, err.Error()) 23 | return 24 | } 25 | 26 | if s.Uuid == "" { 27 | c.String(http.StatusOK, "链接不存在,欢迎关注微信公众号 一个橙子pro") 28 | return 29 | } 30 | 31 | if s.UseStatus == 1 { 32 | c.String(http.StatusOK, "链接已经被原作者废弃,如果您是原作者,请还原废弃状态再使用,欢迎关注微信公众号 一个橙子pro") 33 | return 34 | } 35 | 36 | if s.FileType != end { 37 | c.String(http.StatusOK, "链接不存在,检查后缀是否正确,欢迎关注微信公众号 一个橙子pro") 38 | return 39 | } 40 | 41 | c.String(http.StatusOK, s.Content) 42 | } 43 | 44 | func GetShareUrlData(c *gin.Context) { 45 | u := c.Query("uuid") 46 | s, err := model.GetShareUrl(u) 47 | if err != nil { 48 | c.JSON(http.StatusInternalServerError, result.Fail("联系管理员")) 49 | return 50 | } 51 | c.JSON(http.StatusOK, result.Ok(s, "Ok")) 52 | } 53 | 54 | func GetShareUrls(c *gin.Context) { 55 | us := util.String2Int(c.Query("useStatus")) 56 | ss, err := model.GetShareUrls(us) 57 | if err != nil { 58 | c.JSON(http.StatusInternalServerError, result.Fail("Ok")) 59 | return 60 | } 61 | 62 | c.JSON(http.StatusOK, result.Ok(ss, "Ok")) 63 | } 64 | 65 | func UpdateOrAddShare(c *gin.Context) { 66 | share := model.Share{} 67 | _ = c.ShouldBindJSON(&share) 68 | 69 | r, err := model.UpdateOrAddShare(share) 70 | 71 | if err != nil { 72 | c.JSON(http.StatusInternalServerError, result.Fail("联系管理员")) 73 | return 74 | } 75 | var msg string 76 | if share.Uuid == "" { 77 | msg = "添加成功" 78 | } else { 79 | msg = "链接更新内容成功" 80 | } 81 | c.JSON(http.StatusOK, result.Ok(r, msg)) 82 | } 83 | 84 | func UpdateUseStatus(c *gin.Context) { 85 | share := model.Share{} 86 | 87 | _ = c.ShouldBindJSON(&share) 88 | 89 | r, err := model.UpdateUseStatus(share) 90 | 91 | if err != nil { 92 | c.JSON(http.StatusInternalServerError, result.Fail("联系管理员")) 93 | return 94 | } 95 | 96 | c.JSON(http.StatusOK, result.Ok(r, "成功")) 97 | } 98 | -------------------------------------------------------------------------------- /static/tvshare.txt: -------------------------------------------------------------------------------- 1 | --直播源-- 2 | iptv-org,https://iptv-org.github.io/iptv/languages/zho.m3u 3 | 4 | --收集软件大全-- 5 | yoyodadada,https://yoyodadada.lanzouw.com/u/yoyodadada 6 | 优质APP集散地,https://www.lanzoui.com/u/yoyodadada 7 | 未归类的比较好用的软件,https://www.lanzoui.com/b01b01h9a 8 | hai分享推荐合集,https://pan.lanzou.com/b221497/ 9 | 辉少软件分享合集,https://pan.lanzou.com/b221505 10 | 乐分享软件合集,https://pan.lanzou.com/b215476/ 11 | 大肥精品软件合集,https://pan.lanzou.com/u/qianxun8 12 | 分享社软件分享,https://pan.lanzou.com/b63771/ 13 | 稚初软件集合,https://pan.lanzou.com/b200130 14 | 口令专享软件团队,https://pan.lanzou.com/b240011/ 15 | 安卓破解类软件,https://pan.lanzou.com/b828085 16 | Hs团队破解游戏,https://pan.lanzou.com/b888887 17 | 淘购街软件分享,https://pan.lanzou.com/b165784 18 | 小聪分享社合集,https://pan.lanzou.com/b94326/ 19 | 允晨软件库合集,https://pan.lanzou.com/b54212/ 20 | 软件实验室合集,http://pan.lanzou.com/u/ygtq 21 | 青风软件分享合集,https://pan.lanzou.com/b60564/ 22 | 蜗牛软件库,https://www.lanzoui.com/b1001808 23 | 小说软件合集,https://pan.lanzou.com/b158157/ 24 | 漫画类软件合集,https://pan.lanzou.com/b765262/ 25 | FKzhang_WX模块,https://pan.lanzou.com/b44314 26 | 阿友软件合集,https://pan.lanzou.com/u/aybaba 27 | 无作为分享,https://www.lanzoui.com/u/wuzuowei 28 | 黑科技玩机,https://www.lanzoui.com/b281858 29 | 兜兜线报软件合集,https://www.lanzoui.com/b133841/ 30 | 霖淘购软件共享,https://www.lanzoui.com/b252370/ 31 | 小鹏软件合集,http://www.lanzoui.com/u/xiaopengi 32 | 『BhVip』软件更新合集,http://pan.lanzou.com/u/%E5%BD%AA%E7%85%8Cqq1846055318 33 | 软件梦软件合集,https://www.lanzoui.com/u/Hicro 34 | 嗨分享软件合集,https://www.lanzoui.com/u/%E6%8B%BD%E6%8B%BD 35 | 软件库软件游戏合集,https://www.lanzoui.com/u/rjk 36 | 牛之家,https://www.lanzoui.com/u/memedawq 37 | 星辰软件合集,https://www.lanzoui.com/u/azsoft 38 | 软件大全,https://www.lanzoui.com/u/296742969 39 | 安卓软件合集,https://www.lanzoui.com/u/langman666 40 | 牛牛软件合集,http://www.lanzoui.com/u/36277009 41 | 小银软件库,https://www.lanzoui.com/u/jiek 42 | 优享汇-软件镖局,https://www.lanzoui.com/u/%E6%B1%9F%E4%B8%8A 43 | 新起点软件库,https://www.lanzoui.com/u/xinqidian 44 | 少宇团队,https://www.lanzoui.com/u/shaoyu 45 | 软件分享基地,https://www.lanzoui.com/u/aiwange 46 | A分享-全网软件合集,https://www.lanzoui.com/b205552/ 47 | 秋颜软件库,https://www.lanzoui.com/b341705 48 | 游戏破解合集,https://www.lanzoui.com/b654140 49 | nPlayer,https://www.lanzoui.com/b0cpu28tc 50 | 滚哥网盘资源,https://www.lanzoui.com/b838976 51 | 电视盒子软件合集,https://www.lanzoui.com/b07xdohkf 密码:FULIBA 52 | 安卓精选破解游戏合集,https://www.lanzoui.com/b053xt4vg -------------------------------------------------------------------------------- /ui/src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: color 0.5s, background-color 0.5s; 67 | line-height: 1.6; 68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 70 | font-size: 15px; 71 | text-rendering: optimizeLegibility; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | } 75 | -------------------------------------------------------------------------------- /ui/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/vue-next/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | declare module '@vue/runtime-core' { 7 | export interface GlobalComponents { 8 | AButton: typeof import('ant-design-vue/es')['Button'] 9 | ACard: typeof import('ant-design-vue/es')['Card'] 10 | ACol: typeof import('ant-design-vue/es')['Col'] 11 | AEmpty: typeof import('ant-design-vue/es')['Empty'] 12 | AForm: typeof import('ant-design-vue/es')['Form'] 13 | AFormItem: typeof import('ant-design-vue/es')['FormItem'] 14 | AInput: typeof import('ant-design-vue/es')['Input'] 15 | AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] 16 | ALayout: typeof import('ant-design-vue/es')['Layout'] 17 | ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent'] 18 | ALayoutFooter: typeof import('ant-design-vue/es')['LayoutFooter'] 19 | ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader'] 20 | ARadio: typeof import('ant-design-vue/es')['Radio'] 21 | ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] 22 | ARow: typeof import('ant-design-vue/es')['Row'] 23 | ASpace: typeof import('ant-design-vue/es')['Space'] 24 | ASpin: typeof import('ant-design-vue/es')['Spin'] 25 | ATable: typeof import('ant-design-vue/es')['Table'] 26 | ATabPane: typeof import('ant-design-vue/es')['TabPane'] 27 | ATabs: typeof import('ant-design-vue/es')['Tabs'] 28 | ATextarea: typeof import('ant-design-vue/es')['Textarea'] 29 | IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default'] 30 | IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default'] 31 | IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default'] 32 | IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default'] 33 | IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default'] 34 | Index: typeof import('./src/components/Layout/Index.vue')['default'] 35 | Login: typeof import('./src/components/Login.vue')['default'] 36 | RouterLink: typeof import('vue-router')['RouterLink'] 37 | RouterView: typeof import('vue-router')['RouterView'] 38 | } 39 | } 40 | 41 | export {} 42 | -------------------------------------------------------------------------------- /utils/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/sha1" 6 | "encoding/hex" 7 | "encoding/json" 8 | "fmt" 9 | "github.com/gin-gonic/gin" 10 | "io/ioutil" 11 | "math/rand" 12 | "net/http" 13 | "regexp" 14 | "sort" 15 | "strconv" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | type CitySN struct { 21 | Cip string 22 | Cid string 23 | Cname string 24 | } 25 | 26 | // GetCitySn 获取IP 地区等信息 27 | func GetCitySn() (CitySN, error) { 28 | city := new(CitySN) 29 | 30 | res, err := http.Get("https://pv.sohu.com/cityjson?ie=utf-8") 31 | defer func() { 32 | if err := res.Body.Close(); err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | }() 37 | if err != nil { 38 | return *city, err 39 | } 40 | 41 | r, ioErr := ioutil.ReadAll(res.Body) 42 | if ioErr != nil { 43 | return *city, err 44 | } 45 | 46 | reg := regexp.MustCompile(`var returnCitySN = (.*?);`) 47 | citySnStr := reg.FindStringSubmatch(string(r))[1] 48 | 49 | fmt.Println(citySnStr) 50 | 51 | cityErr := json.Unmarshal([]byte(reg.FindStringSubmatch(string(r))[1]), city) 52 | if cityErr != nil { 53 | return *city, err 54 | } 55 | 56 | return *city, nil 57 | } 58 | 59 | func GetPage(c *gin.Context) (int, int, error) { 60 | var err error 61 | current, errC := strconv.Atoi(c.DefaultQuery("current", "1")) 62 | err = errC 63 | 64 | size, errS := strconv.Atoi(c.DefaultQuery("size", "10")) 65 | err = errS 66 | return current, size, err 67 | } 68 | 69 | //Randomstring 取得随机字符串:使用字符串拼接 70 | func Randomstring(length int) string { 71 | if length < 1 { 72 | return "" 73 | } 74 | char := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 75 | charArr := strings.Split(char, "") 76 | charlen := len(charArr) 77 | ran := rand.New(rand.NewSource(time.Now().Unix())) 78 | 79 | var rchar string = "" 80 | for i := 1; i <= length; i++ { 81 | rchar = rchar + charArr[ran.Intn(charlen)] 82 | } 83 | return rchar 84 | } 85 | 86 | // CheckSignature 微信公众号签名检查 87 | func CheckSignature(signature, timestamp, nonce, token string) bool { 88 | arr := []string{timestamp, nonce, token} 89 | // 字典序排序 90 | sort.Strings(arr) 91 | 92 | n := len(timestamp) + len(nonce) + len(token) 93 | var b strings.Builder 94 | b.Grow(n) 95 | for i := 0; i < len(arr); i++ { 96 | b.WriteString(arr[i]) 97 | } 98 | 99 | return Sha1(b.String()) == signature 100 | } 101 | 102 | // Sha1 进行Sha1编码 103 | func Sha1(str string) string { 104 | h := sha1.New() 105 | h.Write([]byte(str)) 106 | return hex.EncodeToString(h.Sum(nil)) 107 | } 108 | 109 | // Md5 进行Md5编码 110 | func Md5(str string) string { 111 | m := md5.New() 112 | m.Write([]byte(str)) 113 | return hex.EncodeToString(m.Sum(nil)) 114 | } 115 | -------------------------------------------------------------------------------- /ui/auto-import.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by 'unplugin-auto-import' 2 | export {} 3 | declare global { 4 | const EffectScope: typeof import('vue')['EffectScope'] 5 | const computed: typeof import('vue')['computed'] 6 | const createApp: typeof import('vue')['createApp'] 7 | const customRef: typeof import('vue')['customRef'] 8 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 9 | const defineComponent: typeof import('vue')['defineComponent'] 10 | const effectScope: typeof import('vue')['effectScope'] 11 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 12 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 13 | const h: typeof import('vue')['h'] 14 | const inject: typeof import('vue')['inject'] 15 | const isProxy: typeof import('vue')['isProxy'] 16 | const isReactive: typeof import('vue')['isReactive'] 17 | const isReadonly: typeof import('vue')['isReadonly'] 18 | const isRef: typeof import('vue')['isRef'] 19 | const markRaw: typeof import('vue')['markRaw'] 20 | const nextTick: typeof import('vue')['nextTick'] 21 | const onActivated: typeof import('vue')['onActivated'] 22 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 23 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 24 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 25 | const onDeactivated: typeof import('vue')['onDeactivated'] 26 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 27 | const onMounted: typeof import('vue')['onMounted'] 28 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 29 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 30 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 31 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 32 | const onUnmounted: typeof import('vue')['onUnmounted'] 33 | const onUpdated: typeof import('vue')['onUpdated'] 34 | const provide: typeof import('vue')['provide'] 35 | const reactive: typeof import('vue')['reactive'] 36 | const readonly: typeof import('vue')['readonly'] 37 | const ref: typeof import('vue')['ref'] 38 | const resolveComponent: typeof import('vue')['resolveComponent'] 39 | const shallowReactive: typeof import('vue')['shallowReactive'] 40 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 41 | const shallowRef: typeof import('vue')['shallowRef'] 42 | const toRaw: typeof import('vue')['toRaw'] 43 | const toRef: typeof import('vue')['toRef'] 44 | const toRefs: typeof import('vue')['toRefs'] 45 | const triggerRef: typeof import('vue')['triggerRef'] 46 | const unref: typeof import('vue')['unref'] 47 | const useAttrs: typeof import('vue')['useAttrs'] 48 | const useCssModule: typeof import('vue')['useCssModule'] 49 | const useCssVars: typeof import('vue')['useCssVars'] 50 | const useSlots: typeof import('vue')['useSlots'] 51 | const watch: typeof import('vue')['watch'] 52 | const watchEffect: typeof import('vue')['watchEffect'] 53 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 54 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 55 | } 56 | -------------------------------------------------------------------------------- /ui/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 52 | 153 | -------------------------------------------------------------------------------- /ui/src/views/Edit.vue: -------------------------------------------------------------------------------- 1 | 53 | 143 | 149 | -------------------------------------------------------------------------------- /api/alipay/f2f.go: -------------------------------------------------------------------------------- 1 | package alipay 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "github.com/go-pay/gopay" 8 | "github.com/go-pay/gopay/alipay" 9 | "github.com/go-pay/gopay/pkg/xlog" 10 | "net/http" 11 | "os" 12 | "strconv" 13 | "time" 14 | "wtv-online/model" 15 | "wtv-online/utils/result" 16 | ) 17 | 18 | var signType string 19 | var payUrl string 20 | var appid string 21 | var notifyUrl string 22 | var rsaPrivateKey string 23 | var alipayPublicKey string 24 | var client alipay.Client 25 | 26 | func InitAliPayInfo() { 27 | /*** 请填写以下配置信息 ***/ 28 | signType = os.Getenv("ALI_PAY_SIGN_TYPE") //签名算法类型,支持RSA2和RSA,推荐使用RSA2 29 | payUrl = os.Getenv("ALI_PAY_URL") 30 | appid = os.Getenv("ALI_PAY_APPID") // 沙箱 2016110300789385 https://open.alipay.com 账户中心->密钥管理->开放平台密钥,填写添加了当面付应用的APPID 31 | notifyUrl = os.Getenv("ALI_PAY_NOTIFY_URL") //付款成功后的异步回调地址 //付款金额,单位:元 //订单标题 32 | //商户私钥,填写对应签名算法类型的私钥,如何生成密钥参考:https://docs.open.alipay.com/291/105971和https://docs.open.alipay.com/200/105310 33 | rsaPrivateKey = os.Getenv("ALI_PAY_RAS_PRIVATE_KEY") 34 | //支付宝公钥,登录支付宝开放平台,账户中心->密钥管理->开放平台密钥,找到对应的应用,在接口内容加密方式处查看支付宝公钥 35 | alipayPublicKey = os.Getenv("ALI_PAY_RAS_PUBLIC_KEY") 36 | 37 | _client, err := alipay.NewClient(appid, rsaPrivateKey, false) 38 | client = *_client 39 | if err != nil { 40 | xlog.Error(err) 41 | return 42 | } 43 | //配置公共参数 44 | client.SetCharset("utf-8"). 45 | SetSignType(alipay.RSA2). 46 | SetNotifyUrl(notifyUrl) 47 | } 48 | 49 | //生成订单号 50 | func order() string { 51 | now := time.Now() 52 | return fmt.Sprintf("%s%08x%05x", "", now.Unix(), now.UnixNano()%0x100000) 53 | } 54 | 55 | func DoPay(c *gin.Context) { 56 | var payAmount float64 57 | var orderName string 58 | 59 | ctx := context.Background() 60 | if c.Query("totalFee") != "" { 61 | payAmount, _ = strconv.ParseFloat(c.Query("totalFee"), 64) 62 | } 63 | outTradeNo := order() 64 | if c.Query("outTradeNo") != "" { 65 | outTradeNo = c.Query("outTradeNo") 66 | } 67 | if c.Query("orderName") != "" { 68 | orderName = c.Query("orderName") 69 | } 70 | 71 | bm := make(gopay.BodyMap) 72 | bm.Set("subject", orderName) 73 | bm.Set("out_trade_no", outTradeNo) 74 | bm.Set("total_amount", payAmount) 75 | //创建订单 76 | aliRsp, err := client.TradePrecreate(ctx, bm) 77 | if err != nil { 78 | xlog.Error("err:", err) 79 | c.JSON(http.StatusInternalServerError, result.Fail(err.Error())) 80 | return 81 | } 82 | 83 | _ = model.AddFacePay(model.FacePay{OutTradeNo: outTradeNo, TradeName: orderName, OutTradeMoney: payAmount}) 84 | xlog.Debug("aliRsp:", *aliRsp) 85 | xlog.Debug("aliRsp.QrCode:", aliRsp.Response.QrCode) 86 | xlog.Debug("aliRsp.OutTradeNo:", aliRsp.Response.OutTradeNo) 87 | 88 | c.JSON(http.StatusOK, result.Ok(gin.H{ 89 | "qrCode": aliRsp.Response.QrCode, 90 | "outTradeNo": aliRsp.Response.OutTradeNo, 91 | }, "生成订单成功")) 92 | } 93 | 94 | // Notify 异步回调通知处理 95 | func Notify(c *gin.Context) { 96 | 97 | notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法 98 | if err != nil { 99 | xlog.Error(err) 100 | } 101 | 102 | fmt.Println(notifyReq) 103 | 104 | // 公钥模式验签 105 | // 注意:APP支付,手机网站支付,电脑网站支付 不支持同步返回验签 106 | // aliPayPublicKey:支付宝平台获取的支付宝公钥 107 | // signData:待验签参数,aliRsp.SignData 108 | // sign:待验签sign,aliRsp.Sign 109 | _, err2 := alipay.VerifySign(alipayPublicKey, notifyReq) 110 | 111 | if err2 != nil { 112 | xlog.Error(err) 113 | } 114 | outTradeNo := notifyReq.Get("out_trade_no") 115 | outTradeMoney, _ := strconv.ParseFloat(notifyReq.Get("out_trade_money"), 64) 116 | appId := notifyReq.Get("app_id") 117 | authAppId := notifyReq.Get("auth_app_id") 118 | buyerId := notifyReq.Get("buyer_id") 119 | buyerLogonId := notifyReq.Get("buyer_logon_id") 120 | buyerPayAmount, _ := strconv.ParseFloat(notifyReq.Get("buyer_pay_amount"), 64) 121 | gmtCreate, _ := time.Parse("2006-01-02 15:04:05", notifyReq.Get("gmt_create")) 122 | gmtPayment, _ := time.Parse("2006-01-02 15:04:05", notifyReq.Get("gmt_payment")) 123 | sellerEmail := notifyReq.Get("seller_email") 124 | tradeNo := notifyReq.Get("trade_no") 125 | tradeStatus := notifyReq.Get("trade_status") 126 | //tradeName := notifyReq.Get("trade_name") 127 | 128 | _ = model.UpdateFacePay(outTradeNo, model.FacePay{ 129 | OutTradeMoney: outTradeMoney, 130 | AppId: appId, 131 | AuthAppId: authAppId, 132 | BuyerId: buyerId, 133 | BuyerLogonId: buyerLogonId, 134 | BuyerPayAmount: buyerPayAmount, 135 | GmtCreate: gmtCreate, 136 | GmtPayment: gmtPayment, 137 | SellerEmail: sellerEmail, 138 | TradeNo: tradeNo, 139 | TradeStatus: tradeStatus, 140 | }) 141 | //程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h); 142 | c.String(http.StatusOK, "success") 143 | } 144 | -------------------------------------------------------------------------------- /ui/src/hooks/axios/index.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import type { 3 | AxiosError, 4 | AxiosRequestConfig, 5 | AxiosResponse, 6 | CancelTokenSource, 7 | } from "axios"; 8 | import { ref, shallowRef } from "vue"; 9 | import { debounce } from "howtools"; 10 | export * from "./help"; 11 | 12 | export type RequestInterceptor = ( 13 | config: AxiosRequestConfig 14 | ) => AxiosRequestConfig; 15 | export type ResponseInterceptor = (response: AxiosResponse) => AxiosResponse; 16 | export type ErrResponseInterceptor = (err: any) => any; 17 | 18 | export interface HowAxiosRequestConfig extends AxiosRequestConfig { 19 | path?: any; 20 | } 21 | 22 | export interface HowVesAxiosOptions { 23 | instanceConfig: AxiosRequestConfig; 24 | requestInterceptor?: RequestInterceptor; 25 | responseInterceptor?: ResponseInterceptor; 26 | errResponseInterceptor?: ErrResponseInterceptor; 27 | } 28 | 29 | export interface HowVesExRequestOptions { 30 | immediate?: boolean; 31 | delay?: number; 32 | isDebounce?: boolean; 33 | defaultVal?: any; 34 | } 35 | 36 | export function createAxios(options: HowVesAxiosOptions) { 37 | const { 38 | instanceConfig: config, 39 | requestInterceptor, 40 | responseInterceptor, 41 | errResponseInterceptor, 42 | } = options; 43 | 44 | const server = axios.create({ 45 | ...config, 46 | baseURL: config.baseURL, 47 | }); 48 | 49 | // 请求拦截器 50 | server.interceptors.request.use((config) => { 51 | const c = config as HowAxiosRequestConfig; 52 | for (const key in c.path) { 53 | config.url = config.url?.replace(`{${key}}`, c.path[key]); 54 | } 55 | delete c.path; 56 | if (!requestInterceptor) return c; 57 | return requestInterceptor(c as AxiosRequestConfig); 58 | }); 59 | 60 | // 响应拦截器 61 | server.interceptors.response.use( 62 | (response) => { 63 | // 设置不允许修改原始data 64 | responseInterceptor && responseInterceptor(response); 65 | return response; 66 | }, 67 | (err: any) => { 68 | // 失败拦截处理 69 | errResponseInterceptor && errResponseInterceptor(err); 70 | // return err 71 | } 72 | ); 73 | 74 | // Axios hook 75 | /** 76 | * @param {AxiosRequestConfig} config 77 | * @param {HowVesExRequestOptions} options? 78 | * @returns 79 | */ 80 | function useAxiosRequest( 81 | config: HowAxiosRequestConfig, 82 | options?: HowVesExRequestOptions 83 | ) { 84 | const { isDebounce = true, defaultVal = {} } = options || {}; 85 | 86 | let lastConf = config; //最后一次发出请求的配置 87 | const isLoading = shallowRef(false); 88 | const isFinished = shallowRef(false); 89 | const aborted = shallowRef(false); // 请求被中断 90 | const cancelToken: CancelTokenSource = axios.CancelToken.source(); 91 | 92 | const loading = (loading: boolean) => { 93 | isLoading.value = loading; 94 | isFinished.value = !loading; 95 | }; 96 | 97 | const abort = (message?: string) => { 98 | if (isFinished.value || !isLoading.value) return; 99 | 100 | cancelToken.cancel(message); 101 | aborted.value = true; 102 | isLoading.value = false; 103 | isFinished.value = false; 104 | }; 105 | 106 | const response = ref>(); //axios响应 107 | const data = ref(defaultVal); //响应数据 108 | const error = ref>(); // axios 错误响应 109 | const edata = ref(); // axios 错误响应数据 110 | 111 | // 不是节流的方式 112 | const preRequest = ({ 113 | params: p, 114 | data: d, 115 | path: pv, 116 | }: HowAxiosRequestConfig) => { 117 | const c = { ...config, params: p, data: d, path: pv }; 118 | server 119 | .request({ ...c, cancelToken: cancelToken.token }) 120 | .then((r) => { 121 | response.value = r; 122 | data.value = r.data; 123 | loading(false); 124 | }) 125 | .catch((e: AxiosError) => { 126 | error.value = e as any; 127 | edata.value = e.response ? e.response.data : ("" as any); 128 | loading(false); 129 | }); 130 | }; 131 | 132 | const request = debounce(preRequest, (options && options.delay) || 1); 133 | 134 | const execute = ( 135 | config: Pick = { 136 | params: {}, 137 | data: {}, 138 | path: {}, 139 | } 140 | ): Promise => { 141 | lastConf = config; 142 | 143 | loading(true); 144 | 145 | if (isDebounce) { 146 | request(config); 147 | } else { 148 | preRequest(config); 149 | } 150 | return new Promise((resolve) => { 151 | const resultInterval = setInterval(() => { 152 | if (isFinished.value) { 153 | clearInterval(resultInterval); 154 | resolve(data.value as T); 155 | } 156 | }, 100); 157 | }); 158 | }; 159 | 160 | // 立即执行 161 | if (options?.immediate) 162 | execute({ 163 | path: config.path, 164 | params: config.params, 165 | data: config.data, 166 | }); 167 | 168 | // 重新加载上次请求 169 | function reload() { 170 | execute(lastConf); 171 | } 172 | 173 | return { 174 | response, 175 | data, 176 | error, 177 | edata, 178 | execute, 179 | reload, 180 | aborted, 181 | abort, 182 | finished: isFinished, 183 | loading: isLoading, 184 | }; 185 | } 186 | 187 | return { 188 | server, 189 | useAxiosRequest, 190 | }; 191 | } 192 | -------------------------------------------------------------------------------- /utils/util/f2f.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/rand" 7 | "crypto/rsa" 8 | "crypto/sha256" 9 | "crypto/x509" 10 | "encoding/base64" 11 | "encoding/json" 12 | "encoding/pem" 13 | "errors" 14 | "fmt" 15 | "io/ioutil" 16 | "log" 17 | "net/http" 18 | "net/url" 19 | "sort" 20 | "strconv" 21 | "strings" 22 | "time" 23 | ) 24 | 25 | /*** 请填写以下配置信息 ***/ 26 | var appid string = "2016103002409228" //https://open.alipay.com 账户中心->密钥管理->开放平台密钥,填写添加了当面付应用的APPID 27 | var notifyUrl string = "http://www.example.com/notify" //付款成功后的异步回调地址 28 | var payAmount float64 = 0.01 //付款金额,单位:元 29 | var orderName string = "支付测试" //订单标题 30 | var signType string = "RSA2" //签名算法类型,支持RSA2和RSA,推荐使用RSA2 31 | 32 | //商户私钥,填写对应签名算法类型的私钥,如何生成密钥参考:https://docs.open.alipay.com/291/105971和https://docs.open.alipay.com/200/105310 33 | var rsaPrivateKey string = "MIIEpQIBAAKCAQEAixs8PTApD+d/OVxCIc2CgZaXQHzfUZP4qabKJg9I9pCeW4bZbJqfF1zulfJtH9aK0rRT3CjOxRQUxRJ2AW4WnpIMyRquDWhZpMxj65Kv/Z+o6XDeYepOf5Ue8XkbodUssFJiWw24jEQXiD0TYkgKhmPYcEpTAZ4Vf0bAPpPzOEPk7uyoqQqOj4mYhSVPfT59wI1TdjV+Jaj7Mgw7J5PzZ4mT0p9yqF230+6BcjIXiYsIrEZNok39BKr7bPtpMGGimnypb+z1qlnrTMOBm7c9Y+YnHYTuOXy9NxJ6A1tut2XFXY/LXtYQlZV+AD2GeESWwEqHZ9spw5oVKU6NLZM1QwIDAQABAoIBAGnmqRyYHpqqDdQ4ZWxgjVtkru98YXM0qJlXjgwdlNmJTdW2Oq6QpE9gA48KSYN0jKlOeY1hT9D/sbQ6krYR/oxn+e3cLuesKpros6UzPmJie3CE6fg3Ld25RB5nqd6xcN0nSxOwxW+boyKoBKrM155qQutx2fGgTRNhZJf/gKmy/wVk8dSjME8Qdy4U1upfouknqAcl48b/mwVCmvysZAaCCmBLem/DYJ4nj4W63XDmEPwcNhmtQvXU262AV6QUECRJnwIedaR119T1cVkRrN1jLb0OQcG4UFsBCOE3QNLmL7qlVzjMcblub2XZRgJHYKgrZOJvnm3UuzQfbziEVLkCgYEAw1HRUvx+7XYu8+3cxm9OvDv1O/elPS84Ws7feF3lFRQTovDXqpEYiOL26AvScsoYu+Xz9ukMb8xD2k9z1SSfPBwSGxFNhzwuFWyBOHAkvStiIrDpFlgMXGa4tIFuHfW5VoT243myioYJfyDnrxjLCMwTpxuQsey5QEURzvHnWWUCgYEAtlKpW+pZFZN86qgkxb9qERhrfLrJRlSKSARoVd7+x6cZuPE5s3U+v0Xk7eKXuBplSH/rH3Vcb0kkGxrpprCUB9NPPL4ACzfXiC24TUr6l+kBfAgFY3VztoKG71wR6M6mVtwPcpMSCNw9ykIEGbLDmEpCuSuG6WlLillzhQnOPYcCgYEAtvy1JpFF/QTHEmLbftETK8jkhQ0LMtpFatX7iRI2p9vKVdJbywHqwXwZo+ZHgKGUMJ1MRhuqOiddvm7Hb33C7RbDY7Z/Mt6PsiSYn5543P2DEEs75GITGpJmIz5LQyhOPh+OCimeuqa+yOq0BFgvB4viMaHimr7UA8Fx4LbDbv0CgYEAoKJxvp7ZwC3C2Egupd+AxvEZ+rnjDcj8UHk5p7aiCSvLeOtnqBo7n3AoRALpI3Z2GAtED+Jp/06UtiDfDVL0CXT+kgjgpwGbLssBX3AZG3BaM3oprThVK88SrZ8T1GcFnO+j2JGFSTMqaaICVNXwiKOc5+JWqh1wRcOMCQHbqM8CgYEAvhQukKrWXR9467OT3NOVdYzA9FDFtTKC+aRhPM5oYzZbCyboXrl3Uycdsi8YSCCXETdVzdGI9+qb15UPdlmxs4CTZ4DvUA3XDjnUXea7P1EwSopdz667la0IO0Jk7tAZfFCfMKAXuI0YuxgsAq6lWg82LQSz3JKfknYyrRNPB7w=" 34 | 35 | //支付宝公钥,登录支付宝开放平台,账户中心->密钥管理->开放平台密钥,找到对应的应用,在接口内容加密方式处查看支付宝公钥 36 | var alipayPublicKey string = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAixs8PTApD+d/OVxCIc2CgZaXQHzfUZP4qabKJg9I9pCeW4bZbJqfF1zulfJtH9aK0rRT3CjOxRQUxRJ2AW4WnpIMyRquDWhZpMxj65Kv/Z+o6XDeYepOf5Ue8XkbodUssFJiWw24jEQXiD0TYkgKhmPYcEpTAZ4Vf0bAPpPzOEPk7uyoqQqOj4mYhSVPfT59wI1TdjV+Jaj7Mgw7J5PzZ4mT0p9yqF230+6BcjIXiYsIrEZNok39BKr7bPtpMGGimnypb+z1qlnrTMOBm7c9Y+YnHYTuOXy9NxJ6A1tut2XFXY/LXtYQlZV+AD2GeESWwEqHZ9spw5oVKU6NLZM1QwIDAQAB" 37 | 38 | /*** 配置结束 ***/ 39 | 40 | type AlipayService struct { 41 | appId string 42 | notifyUrl string 43 | charset string 44 | rsaPrivateKey *rsa.PrivateKey 45 | alipayPublicKey *rsa.PublicKey 46 | totalFee float64 47 | outTradeNo string 48 | orderName string 49 | } 50 | 51 | func (this *AlipayService) SetAppId(appId string) { 52 | this.appId = appId 53 | } 54 | 55 | func (this *AlipayService) SetNotifyUrl(notifyUrl string) { 56 | this.notifyUrl = notifyUrl 57 | } 58 | 59 | func (this *AlipayService) SetCharset(charset string) { 60 | this.charset = charset 61 | } 62 | 63 | func (this *AlipayService) SetRsaPrivateKey(keyString string) { 64 | 65 | privateKey, err := ParsePrivateKey(FormatPrivateKey(keyString)) 66 | if err != nil { 67 | panic(err) 68 | } 69 | this.rsaPrivateKey = privateKey 70 | } 71 | 72 | func (this *AlipayService) SetAlipayPublicKey(keyString string) { 73 | alipayPublicKey, err := ParsePublicKey(FormatPublicKey(keyString)) 74 | if err != nil { 75 | panic(err) 76 | } 77 | this.alipayPublicKey = alipayPublicKey 78 | } 79 | 80 | func (this *AlipayService) SetTotalFee(totalFee float64) { 81 | this.totalFee = totalFee 82 | } 83 | 84 | func (this *AlipayService) SetOutTradeNo(outTradeNo string) { 85 | this.outTradeNo = outTradeNo 86 | } 87 | 88 | func (this *AlipayService) SetOrderName(orderName string) { 89 | this.orderName = orderName 90 | } 91 | 92 | // GenSign 产生签名 93 | func (this *AlipayService) GenSign(m map[string]string) string { 94 | var data []string 95 | var encryptedBytes []byte 96 | for k, v := range m { 97 | if v != "" && k != "sign" { 98 | data = append(data, fmt.Sprintf(`%s=%s`, k, v)) 99 | } 100 | } 101 | sort.Strings(data) 102 | signData := strings.Join(data, "&") 103 | s := sha256.New() 104 | _, err := s.Write([]byte(signData)) 105 | if err != nil { 106 | panic(err) 107 | } 108 | hashByte := s.Sum(nil) 109 | hashs := crypto.SHA256 110 | rsaPrivateKey := this.rsaPrivateKey 111 | if encryptedBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey, hashs, hashByte); err != nil { 112 | panic(err) 113 | } 114 | return base64.StdEncoding.EncodeToString(encryptedBytes) 115 | } 116 | 117 | func (this *AlipayService) DoPay() (string, error) { 118 | 119 | //请求参数 120 | var bizContent = make(map[string]string) 121 | bizContent["out_trade_no"] = this.outTradeNo 122 | bizContent["total_amount"] = strconv.FormatFloat(float64(this.totalFee), 'f', 2, 64) //2表示保留2位小数 123 | bizContent["subject"] = this.orderName 124 | bizContentJson, err := json.Marshal(bizContent) 125 | if err != nil { 126 | return "", errors.New("json.Marshal: " + err.Error()) 127 | } 128 | 129 | //公共参数 130 | var m = make(map[string]string) 131 | m["app_id"] = this.appId 132 | m["method"] = "alipay.trade.precreate" //接口名称 133 | m["format"] = "JSON" 134 | m["charset"] = this.charset 135 | m["sign_type"] = "RSA2" 136 | m["timestamp"] = time.Now().Format("2006-01-02 15:04:05") 137 | m["version"] = "1.0" 138 | m["notify_url"] = this.notifyUrl 139 | m["biz_content"] = string(bizContentJson) 140 | 141 | //获取签名 142 | sign := this.GenSign(m) 143 | m["sign"] = sign 144 | requestUrl := "https://openapi.alipay.com/gateway.do?charset=" + this.charset 145 | return postData(requestUrl, m), nil 146 | } 147 | 148 | func (this *AlipayService) VerifySign(data url.Values) (ok bool, err error) { 149 | return verifySign(data, this.alipayPublicKey) 150 | } 151 | 152 | func ParsePrivateKey(data []byte) (key *rsa.PrivateKey, err error) { 153 | var block *pem.Block 154 | block, _ = pem.Decode(data) 155 | if block == nil { 156 | return nil, errors.New("private key failed to load") 157 | } 158 | 159 | var priInterface interface{} 160 | priInterface, err = x509.ParsePKCS1PrivateKey(block.Bytes) 161 | if err != nil { 162 | return nil, err 163 | } 164 | key, ok := priInterface.(*rsa.PrivateKey) 165 | if !ok { 166 | return nil, errors.New("private key failed to load") 167 | } 168 | 169 | return key, err 170 | } 171 | 172 | func ParsePublicKey(data []byte) (key *rsa.PublicKey, err error) { 173 | var block *pem.Block 174 | block, _ = pem.Decode(data) 175 | if block == nil { 176 | return nil, errors.New("alipay public key failed to load") 177 | } 178 | 179 | var pubInterface interface{} 180 | pubInterface, err = x509.ParsePKIXPublicKey(block.Bytes) 181 | if err != nil { 182 | return nil, err 183 | } 184 | key, ok := pubInterface.(*rsa.PublicKey) 185 | if !ok { 186 | return nil, errors.New("alipay public key failed to load") 187 | } 188 | 189 | return key, err 190 | } 191 | 192 | func FormatPublicKey(raw string) []byte { 193 | return formatKey(raw, "-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", 64) 194 | } 195 | 196 | func FormatPrivateKey(raw string) []byte { 197 | return formatKey(raw, "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", 64) 198 | } 199 | 200 | func formatKey(raw, prefix, suffix string, lineCount int) []byte { 201 | if raw == "" { 202 | return nil 203 | } 204 | raw = strings.Replace(raw, prefix, "", 1) 205 | raw = strings.Replace(raw, suffix, "", 1) 206 | raw = strings.Replace(raw, " ", "", -1) 207 | raw = strings.Replace(raw, "\n", "", -1) 208 | raw = strings.Replace(raw, "\r", "", -1) 209 | raw = strings.Replace(raw, "\t", "", -1) 210 | 211 | var sl = len(raw) 212 | var c = sl / lineCount 213 | if sl%lineCount > 0 { 214 | c = c + 1 215 | } 216 | 217 | var buf bytes.Buffer 218 | buf.WriteString(prefix + "\n") 219 | for i := 0; i < c; i++ { 220 | var b = i * lineCount 221 | var e = b + lineCount 222 | if e > sl { 223 | buf.WriteString(raw[b:]) 224 | } else { 225 | buf.WriteString(raw[b:e]) 226 | } 227 | buf.WriteString("\n") 228 | } 229 | buf.WriteString(suffix) 230 | return buf.Bytes() 231 | } 232 | 233 | func verifySign(data url.Values, key *rsa.PublicKey) (ok bool, err error) { 234 | sign := data.Get("sign") 235 | 236 | var keys = make([]string, 0, 0) 237 | for key := range data { 238 | if key == "sign" || key == "sign_type" { 239 | continue 240 | } 241 | keys = append(keys, key) 242 | } 243 | 244 | sort.Strings(keys) 245 | 246 | var pList = make([]string, 0, 0) 247 | for _, key := range keys { 248 | pList = append(pList, key+"="+data.Get(key)) 249 | } 250 | var s = strings.Join(pList, "&") 251 | 252 | return verifyData([]byte(s), sign, key) 253 | } 254 | 255 | func verifyData(data []byte, sign string, key *rsa.PublicKey) (ok bool, err error) { 256 | signBytes, err := base64.StdEncoding.DecodeString(sign) 257 | if err != nil { 258 | return false, err 259 | } 260 | 261 | var h = crypto.SHA256.New() 262 | h.Write(data) 263 | var hashed = h.Sum(nil) 264 | 265 | if err = rsa.VerifyPKCS1v15(key, crypto.SHA256, hashed, signBytes); err != nil { 266 | return false, err 267 | } 268 | return true, nil 269 | } 270 | 271 | func postData(requestUrl string, m map[string]string) string { 272 | data := make(url.Values) 273 | for k, v := range m { 274 | data[k] = []string{v} 275 | } 276 | 277 | resp, err := http.PostForm(requestUrl, data) 278 | if err != nil || resp.StatusCode != http.StatusOK { 279 | // 处理错误 280 | return "请求错误" 281 | } 282 | defer resp.Body.Close() 283 | buf := new(bytes.Buffer) 284 | buf.ReadFrom(resp.Body) 285 | return buf.String() 286 | } 287 | 288 | //生成订单号 289 | func Uniqid() string { 290 | now := time.Now() 291 | return fmt.Sprintf("%s%08x%05x", "", now.Unix(), now.UnixNano()%0x100000) 292 | } 293 | 294 | func DoPay(w http.ResponseWriter, r *http.Request) { 295 | params := r.URL.Query() 296 | if params.Get("total_fee") != "" { 297 | payAmount, _ = strconv.ParseFloat(params.Get("total_fee"), 64) 298 | } 299 | outTradeNo := Uniqid() 300 | if params.Get("out_trade_no") != "" { 301 | outTradeNo = params.Get("out_trade_no") 302 | } 303 | if params.Get("order_name") != "" { 304 | orderName = params.Get("order_name") 305 | } 306 | 307 | aliPay := AlipayService{} 308 | aliPay.SetAppId(appid) 309 | aliPay.SetNotifyUrl(notifyUrl) 310 | aliPay.SetCharset("utf-8") 311 | aliPay.SetRsaPrivateKey(rsaPrivateKey) 312 | aliPay.SetAlipayPublicKey(alipayPublicKey) 313 | aliPay.SetTotalFee(payAmount) 314 | aliPay.SetOutTradeNo(outTradeNo) 315 | aliPay.SetOrderName(orderName) 316 | response, err := aliPay.DoPay() 317 | if err != nil { 318 | panic(err) 319 | } 320 | res := []byte(response) 321 | var data interface{} 322 | err = json.Unmarshal(res, &data) 323 | if err != nil { 324 | fmt.Fprintf(w, "JSON解码失败:%v\n", err) 325 | return 326 | } 327 | 328 | //访问解码后的数据 329 | dataDecode, ok := data.(map[string]interface{}) 330 | if ok { 331 | alipayResponse := dataDecode["alipay_trade_precreate_response"].(map[string]interface{}) 332 | code := alipayResponse["code"].(string) 333 | if code == "10000" { 334 | //生成二维码 335 | qrCode := alipayResponse["qr_code"].(string) 336 | url := "http://api.qrserver.com/v1/create-qr-code/?size=300x300&margin=10&data=" + qrCode 337 | html := "
二维码内容:" + qrCode 338 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 339 | fmt.Fprintf(w, html) 340 | return 341 | } else { 342 | msg := alipayResponse["msg"].(string) 343 | subMsg := alipayResponse["sub_msg"].(string) 344 | fmt.Fprintf(w, msg+" : "+subMsg) 345 | return 346 | } 347 | } 348 | 349 | fmt.Fprintf(w, "支付接口出错") 350 | } 351 | 352 | //异步回调通知处理 353 | func Notify(w http.ResponseWriter, r *http.Request) { 354 | defer r.Body.Close() 355 | 356 | //接收post参数 357 | r.ParseForm() 358 | formdata := r.PostForm 359 | 360 | aliPay := AlipayService{} 361 | aliPay.SetCharset("utf-8") 362 | aliPay.SetAlipayPublicKey(alipayPublicKey) 363 | _, err := aliPay.VerifySign(formdata) 364 | if err != nil { 365 | fmt.Fprintf(w, "error:"+err.Error()) 366 | return 367 | } 368 | //处理你的逻辑,例如获取订单号formdata["out_trade_no"][0],订单金额formdata["total_amount"][0]等 369 | //例如这里将回调数据写入到nofity.txt文本中 370 | 371 | ioutil.WriteFile("nofity.txt", []byte(formdata.Encode()), 0644) 372 | //程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h); 373 | fmt.Fprintf(w, "success") 374 | } 375 | 376 | func main() { 377 | //开启web服务器 378 | port := "8089" 379 | http.HandleFunc("/", DoPay) 380 | http.HandleFunc("/notify", Notify) 381 | log.Println("服务器启动成功,监听端口:", port) 382 | err := http.ListenAndServe(":"+port, nil) 383 | if err != nil { 384 | log.Fatal("ListenAndServe:", err) 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 5 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 6 | github.com/duke-git/lancet/v2 v2.0.9 h1:9abrZ97PJLtbBHAlwANU+gV20yIF/h7AXhAbd4u60Ww= 7 | github.com/duke-git/lancet/v2 v2.0.9/go.mod h1:5Nawyf/bK783rCiHyVkZLx+jj8028oVVjLOrC21ZONA= 8 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 9 | github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= 10 | github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 11 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 12 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 13 | github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= 14 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 15 | github.com/glebarez/go-sqlite v1.17.2 h1:gyTyFr2RFFQd2gp6fOOdfnTvUn99zwvVOrQFHA4S+DY= 16 | github.com/glebarez/go-sqlite v1.17.2/go.mod h1:lakPjzvnJ6uSIARV+5dPALDuSLL3879PlzHFMEpbceM= 17 | github.com/glebarez/sqlite v1.4.5 h1:oaJupO4X9iTn4sXRvP5Vs15BNvKh9dx5AQfciKlDvV4= 18 | github.com/glebarez/sqlite v1.4.5/go.mod h1:6D+bB+DdXlEC4mO+pUFJWixVcnrHTIAJ9U6Ynnn4Lxk= 19 | github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= 20 | github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 21 | github.com/go-pay/gopay v1.5.79 h1:w/TaneXqdqyf81RKsaHzT+ZzSZ/z7uvnuaVF9o5pz4g= 22 | github.com/go-pay/gopay v1.5.79/go.mod h1:M6Nlk2VdZHCbWphOw3rtbnz4SiOk6Xvxg6mxwDfg+Ps= 23 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 24 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 25 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 26 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 27 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 28 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 29 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= 30 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 31 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 32 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 33 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 34 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 35 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 36 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 37 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 38 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 39 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 40 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 41 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 42 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 43 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 44 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 45 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 46 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 47 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 48 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 49 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 50 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 51 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 52 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 53 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 54 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 55 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 56 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 57 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 58 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 59 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 60 | github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 61 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 62 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 63 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 64 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 65 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 66 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 67 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= 68 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 69 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 70 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 71 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 72 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 73 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 74 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 75 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 76 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 77 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 78 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 79 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 80 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 81 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 82 | golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= 83 | golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 84 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 85 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 86 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 87 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 88 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 89 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 90 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 91 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 92 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 93 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 94 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 95 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 96 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 97 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 98 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 99 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 100 | golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 h1:D1v9ucDTYBtbz5vNuBbAhIMAGhQhJ6Ym5ah3maMVNX4= 101 | golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 102 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 103 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 104 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 105 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 106 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 107 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 108 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 109 | golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 110 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 111 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 112 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 113 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 114 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 115 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 116 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 117 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 118 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 119 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 120 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 121 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 122 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 123 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 124 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 125 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 126 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 127 | gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM= 128 | gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 129 | lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 130 | modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= 131 | modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= 132 | modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= 133 | modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= 134 | modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= 135 | modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= 136 | modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= 137 | modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= 138 | modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= 139 | modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= 140 | modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= 141 | modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= 142 | modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= 143 | modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 144 | modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= 145 | modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 146 | modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= 147 | modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= 148 | modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 149 | modernc.org/sqlite v1.17.2 h1:TjmF36Wi5QcPYqRoAacV1cAyJ7xB/CD0ExpVUEMebnw= 150 | modernc.org/sqlite v1.17.2/go.mod h1:GOQmuiXd6pTTes1Fi2s9apiCcD/wbKQtBZ0Nw6/etjM= 151 | modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= 152 | modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= 153 | modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 154 | modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= 155 | --------------------------------------------------------------------------------