├── 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 |
2 |
12 |
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 |
2 |
3 |
4 |
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 |
3 |
19 |
20 |
--------------------------------------------------------------------------------
/ui/src/components/icons/IconCommunity.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/ui/src/components/icons/IconDocumentation.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
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 |
2 |
3 |
4 |
5 |
6 | {{ data.title }}
7 |
8 |
9 |
10 |
19 |
20 |
21 |
22 |
23 |
24 | {{ data.description }}
25 |
26 |
27 |
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 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/ui/src/components/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
28 |
29 | 登录
30 |
31 |
32 |
33 |
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 |
2 |
3 |
4 |
5 |
6 |
7 | 暂无数据,新增一个吧
8 |
9 |
10 |
11 |
12 |
13 |
14 | 复制链接
15 |
16 |
17 | 修改
18 |
19 |
20 | 废弃
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
36 |
37 | 暂无数据,您还没有废弃的数据
38 |
39 |
40 |
41 |
42 |
43 | 恢复使用
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
153 |
--------------------------------------------------------------------------------
/ui/src/views/Edit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 返回列表页面
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 | m3u
24 | txt
25 | json
26 | yml
27 |
28 |
29 |
30 |
31 | 保存
32 | 清空填写内容
33 | 返回列表页面
34 |
35 |
36 |
37 |
38 |
42 |
43 | 你可以通过以下链接查看生成的内容,访问之前务必点击保存生成链接
44 |
45 | {{
46 | `${location.origin}/api/share/${modelRef.uuid}.${modelRef.fileType}`
47 | }}
48 |
49 |
50 |
51 |
52 |
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 |
--------------------------------------------------------------------------------