feat:完善信息,增加socket

This commit is contained in:
henry
2022-01-27 14:50:52 +08:00
parent 0368be6f11
commit 4ae37b9b4d
30 changed files with 325 additions and 92 deletions

View File

@ -88,14 +88,14 @@ func (*Manage) CompanyForm(c *gin.Context) {
func (*Manage) CompanyExamine(c *gin.Context) {
form := &struct {
manageExamineForm
Kind int `json:"kind" form:"kind" binding:"required"`
Kind int `json:"kind" form:"kind"`
}{}
if err := api.Bind(form)(c); err != nil {
api.APIFailure(err.(error))(c)
return
}
form.Identity = config.TenantUserIdentityForCompany
err := form.handle(api.GetSession()(c).(*session.Admin), map[string]interface{}{"kind": form.Kind})
err := form.handle(api.GetSession()(c).(*session.Admin), nil)
api.APIResponse(err)(c)
}

View File

@ -27,8 +27,9 @@ type (
}
// CompanyDetail 公司企业详细信息
CompanyDetail struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
ID string `json:"id"`
TenantID string `json:"tenant_id"`
InvitedCode string `json:"invited_code"`
*model2.ManageCompany
Industrys []*config.Industry `json:"industrys"`
Keywords []string `json:"keywords"`
@ -60,7 +61,7 @@ func (c *Company) Instance(tenantID uint64, name string, status int, page, pageS
}
var count int64
out, err := mManageCompany.Companys(page, pageSize, &count)
out, err := mManageCompany.Companys(page, pageSize, &count, where...)
if err != nil {
return nil, err
@ -117,6 +118,7 @@ func (c *Company) Form(params *BasicParams, other *config.IdentityForCompany) er
}
}
mManageCompany.TenantID = c.TenantID
mManageCompany.Kind = model2.ManageCompanyKind(other.Kind)
mManageCompany.Image = model2.Image{Image: params.Image}
mManageCompany.Area = model2.Area{
Province: params.Area.Province, City: params.Area.City, District: params.Area.District, Address: params.Area.Address,
@ -172,10 +174,12 @@ func (c *Company) Detail(id uint64) (*CompanyDetail, error) {
return &CompanyDetail{
ID: out.GetEncodeID(),
TenantID: out.GetEncodeTenantID(),
InvitedCode: (&model2.Model{ID: out.InviterID}).GetEncodeID(),
ManageCompany: out.ManageCompany,
Industrys: _industrys,
Keywords: out.GetKeywordAttribute(),
Area: out.FormatBasic(),
Directions: out.GetDirectionAttribute(),
//Area: out.FormatBasic(),
}, nil
}

View File

@ -0,0 +1 @@
package manage

View File

@ -185,12 +185,14 @@ func (c *Examine) Launch(id uint64, identity, status int, remark string, params
}
return orm.GetDB().Transaction(func(tx *gorm.DB) error {
values := map[string]interface{}{
//"examine_status": status,
"examine_status": status,
"examine_remark": remark, "updated_at": time.Now()}
if identity == config.TenantUserIdentityForCompany {
for k, v := range params {
values[k] = v
if params != nil && len(params) > 0 {
if identity == config.TenantUserIdentityForCompany {
for k, v := range params {
values[k] = v
}
}
}
if err = model2.Updates(data.IModel, values, tx); err != nil {

View File

@ -29,7 +29,7 @@ type (
// ManageCompanyDetail 公司企业信息
ManageCompanyDetail struct {
*model.ManageCompany
model.Area
//model.Area
}
)
@ -37,7 +37,7 @@ type (
func (m *ManageCompany) Company(id uint64) (*ManageCompanyDetail, error) {
db := orm.GetDB().Table(m.TableName()+" AS c").
Select("c.*").
Joins(fmt.Sprintf("LEFT JOIN %s AS t ON c.tenant_id = t.id", model.NewSysTenant().TableName())).
//Joins(fmt.Sprintf("LEFT JOIN %s AS t ON c.tenant_id = t.id", model.NewSysTenant().TableName())).
Where("c.id = ?", id)
out := new(ManageCompanyDetail)

View File

@ -51,7 +51,7 @@ func (c *Instance) Login() InstanceLoginCallback {
_session.Identity = params.Identity
_session.SelectIdentity = params.SelectIdentity
service.Publish(config.EventForRedisHashProduce, config.RedisKeyForAccountEnterprise, _session.UIDToString(), _session)
service.Publish(config.EventForRedisHashProduce, config.RedisKeyForAccountEnterprise, _session.GetStringUID(), _session)
return &InstanceLoginReturn{Token: token, TokenEffectTime: utils.StringToInt(config.SystemConfig[config.SysTokenEffectTime])}
}

View File

@ -4,7 +4,6 @@ import (
"SciencesServer/app/api/enterprise/model"
model2 "SciencesServer/app/common/model"
"SciencesServer/app/handle"
"SciencesServer/config"
"SciencesServer/serve/orm"
"SciencesServer/utils"
"errors"
@ -99,7 +98,7 @@ func loginForSmsCaptcha(params *LoginParams, tenantID uint64) (*InstanceLoginPar
RETURNS:
return &InstanceLoginParams{
UID: mUserInstance.UUID,
Avatar: mUserInstance.GetAvatarAttribute(config.SystemConfig[config.SysImageDomain]), Name: mUserInstance.Name, Mobile: mUserInstance.Mobile,
Avatar: mUserInstance.Avatar, Name: mUserInstance.Name, Mobile: mUserInstance.Mobile,
Vip: mUserInstance.Vip, VipStatus: mUserInstance.VipStatus(), VipDeadline: mUserInstance.VipDeadline,
Currency: mUserAssets.Currency, Identity: mUserInstance.Identity, SelectIdentity: mUserIdentity.Identity,
Status: mUserInstance.Status,
@ -139,7 +138,7 @@ func loginForPassword(params *LoginParams, tenantID uint64) (*InstanceLoginParam
}
return &InstanceLoginParams{
UID: mUserInstance.UUID,
Avatar: mUserInstance.GetAvatarAttribute(config.SystemConfig[config.SysImageDomain]), Name: mUserInstance.Name, Mobile: mUserInstance.Mobile,
Avatar: mUserInstance.Avatar, Name: mUserInstance.Name, Mobile: mUserInstance.Mobile,
Vip: mUserInstance.Vip, VipStatus: mUserInstance.VipStatus(), VipDeadline: mUserInstance.VipDeadline,
Currency: mUserAssets.Currency, Identity: mUserInstance.Identity, SelectIdentity: mUserIdentity.Identity,
Status: mUserInstance.Status,

View File

@ -2,14 +2,9 @@ package manage
import (
"SciencesServer/app/api/enterprise/model"
"SciencesServer/app/basic/config"
"SciencesServer/app/basic/controller"
model2 "SciencesServer/app/common/model"
"SciencesServer/app/service"
"SciencesServer/app/session"
config2 "SciencesServer/config"
"errors"
"strings"
)
// Company 公司信息
@ -102,53 +97,6 @@ func (c *Company) Product(id uint64, page, pageSize int) ([]*CompanyProductInfo,
return list, nil
}
// Match 匹配信息
func (c *Company) Match(title string, industrys, keywords []string) (*controller.ReturnPages, error) {
params := strings.Join([]string{
title, strings.Join(industrys, ","), strings.Join(keywords, ","),
}, ",")
manage := service.NewESManage(
service.WithManageIdentity(config.TenantUserIdentityForCompany),
)
out, count, err := manage.Search(0, 0, map[string]interface{}{
"title": params, "industry": params, "keyword": params, "research": params,
})
if err != nil {
return nil, err
}
ids := make([]uint64, 0)
for _, v := range out.([]interface{}) {
val := v.(*service.ESManage)
ids = append(ids, val.ID)
}
mManageCompany := model.NewManageCompany()
companys := make([]*model2.ManageCompany, 0)
if err := model2.ScanFields(mManageCompany.ManageCompany, &companys, []string{"id", "kind", "name", "image", "url", "keyword"},
&model2.ModelWhereOrder{
Where: model2.NewWhereIn("id", ids),
Order: model2.NewOrder("id", model2.OrderModeToDesc),
}, &model2.ModelWhereOrder{
Where: model2.NewWhere("examine_status", model2.ExamineStatusForAgree),
}); err != nil {
return nil, err
}
list := make([]*CompanyMatchInfo, 0)
for _, v := range companys {
list = append(list, &CompanyMatchInfo{
ID: v.GetEncodeID(), Kind: v.Kind, Name: v.Name,
Image: v.Image.Analysis(config2.SystemConfig[config2.SysImageDomain]),
Url: v.Url,
Keywords: v.GetKeywordAttribute(),
})
}
return &controller.ReturnPages{Data: list, Count: count}, nil
}
func NewCompany() CompanyHandle {
return func(session *session.Enterprise) *Company {
return &Company{

View File

@ -82,6 +82,7 @@ func (c *Company) Launch(params *BasicParams, inviterID uint64, other *config.Id
}
mManageCompany.TenantID = c.tenantID
mManageCompany.InviterID = inviterID
mManageCompany.Kind = model2.ManageCompanyKind(other.Kind)
mManageCompany.Name = params.Name
mManageCompany.Code = params.Code
mManageCompany.Image = model2.Image{Image: params.Image}

View File

@ -235,7 +235,7 @@ func (c *Identity) Switch(identity int) error {
}
}
c.SelectIdentity = identity
service.Publish(config2.EventForAccountLoginProduce, config2.RedisKeyForAccountEnterprise, c.UIDToString(), c.Enterprise)
service.Publish(config2.EventForAccountLoginProduce, config2.RedisKeyForAccountEnterprise, c.GetStringUID(), c.Enterprise)
return nil
}
return errors.New("操作错误,无效的身份信息")

View File

@ -5,6 +5,7 @@ import (
model2 "SciencesServer/app/common/model"
"SciencesServer/app/handle"
"SciencesServer/app/session"
"SciencesServer/config"
"SciencesServer/utils"
"errors"
"time"
@ -36,11 +37,13 @@ type (
// Info 基本信息
func (c *Instance) Info() *InstanceInfo {
out := &InstanceInfo{Avatar: c.Avatar, Name: c.Name,
out := &InstanceInfo{
Name: c.Name,
Avatar: (&model2.UserInstance{Avatar: c.Avatar}).GetAvatarAttribute(config.SystemConfig[config.SysImageDomain]),
Currency: c.Currency,
Identity: c.Identity,
SelectIdentity: c.SelectIdentity}
SelectIdentity: c.SelectIdentity,
}
if c.IsVip() {
out.Vip = c.Vip
out.VipDays = utils.DiffTimeDays(time.Now(), c.VipDeadline)

View File

@ -48,7 +48,7 @@ func (m *UserIdentity) Expert(page, pageSize int, count *int64, where ...*model.
// LastChooseInfo 最后一次选中的信息
func (m *UserIdentity) LastChooseInfo(uid uint64) error {
_, err := model.FirstField(m.UserIdentity, []string{"id", "uuid", "identity"},
_, err := model.FirstField(m.UserIdentity, []string{"id", "identity"},
model.NewWhere("uid", uid),
model.NewWhere("is_selected", model.UserIdentitySelectedForYes))
return err

View File

@ -148,7 +148,7 @@ func paper(uids []uint64, page, pageSize int) (*controller.ReturnPages, error) {
var count int64
if err := model2.PagesFields(mTechnologyPaper.TechnologyPaper, &out, []string{"id", "title", "ext", "author", "keyword"},
if err := model2.PagesFields(mTechnologyPaper.TechnologyPaper, &out, []string{"id", "title", "ext", "author", "keyword", "remark", "publish_at"},
page, pageSize, &count, &model2.ModelWhereOrder{
Where: model2.NewWhereIn("uid", uids),
Order: model2.NewOrder("id", model2.OrderModeToDesc),
@ -234,7 +234,7 @@ func cooperateDetail(id uint64) (*CooperateDetailInfo, error) {
papers := make([]*model2.TechnologyPaper, 0)
if err = model2.ScanFields(mTechnologyPaper.TechnologyPaper, &papers, []string{"id", "title", "ext", "keyword"},
if err = model2.ScanFields(mTechnologyPaper.TechnologyPaper, &papers, []string{"id", "title", "ext", "keyword", "remark", "publish_at"},
&model2.ModelWhereOrder{
Where: model2.NewWhereIn("id", strings.Split(mManageCooperateEnterprise.Paper, ",")),
Order: model2.NewOrder("id", model2.OrderModeToDesc),
@ -244,6 +244,7 @@ func cooperateDetail(id uint64) (*CooperateDetailInfo, error) {
for _, v := range papers {
out.Paper = append(out.Paper, &technology.PaperInfo{
ID: v.GetEncodeID(), Title: v.Title, Ext: v.Ext, Keywords: v.GetKeywordAttribute(),
Remark: v.Remark, PublishAt: v.PublishAt,
})
}
return out, nil

View File

@ -1,12 +1,16 @@
package technology
import "time"
type Paper struct{}
type (
PaperInfo struct {
ID string `json:"id"`
Title string `json:"title"`
Ext string `json:"ext"`
Keywords []string `json:"keywords"`
ID string `json:"id"`
Title string `json:"title"`
Ext string `json:"ext"`
Keywords []string `json:"keywords"`
Remark string `json:"remark"`
PublishAt time.Time `json:"publish_at"`
}
)

View File

@ -0,0 +1,84 @@
package api
import (
"SciencesServer/app/basic/entity"
"SciencesServer/app/logic"
"SciencesServer/app/service"
"SciencesServer/config"
"SciencesServer/serve/logger"
"SciencesServer/utils"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"net/http"
)
type Websocket struct{}
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func (*Websocket) Ws(key string, session logic.ISession) func(ctx *gin.Context) {
return func(c *gin.Context) {
conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.ErrorF("Conn错误【%s】", err)
return
}
token := c.Query(config.APIRequestToken)
resp := &entity.Message{
Message: "Success",
}
if token == "" {
resp.Code = -1
resp.Message = "Token异常无权限链接"
_ = conn.WriteMessage(websocket.TextMessage, utils.AnyToByte(&service.Message{
Prefix: resp.Prefix(),
INotice: resp,
}))
return
}
if err = service.NewAuthToken(token).Auth(key, session); err != nil {
resp.Code = -1
resp.Message = err.Error()
_ = conn.WriteMessage(websocket.TextMessage, utils.AnyToByte(&service.Message{
Prefix: resp.Prefix(),
INotice: resp,
}))
return
}
client := service.NewWebsocket(session.GetStringUID(), conn)
service.HubMessage.RegisterHandle(client)
// 连接成功通知
_ = conn.WriteMessage(websocket.TextMessage, utils.AnyToByte(&service.Message{
Prefix: resp.Prefix(),
INotice: resp,
}))
go client.Write()
}
}
func (*Websocket) Publish(c *gin.Context) {
//key := c.Query("key")
fmt.Println(1232132132132132131)
service.HubMessage.BroadcastHandle(&entity.Message{
Code: 0,
Message: "你有一条新的待办事项",
})
//service.HubMessage.EmitHandle(&service.HubEmit{
// ID: "2118935188548358144",
// Msg: &entity.Message{
// Code: 0,
// Message: "你有一条新的待办事项",
// },
//})
APISuccess()(c)
}

View File

@ -17,6 +17,7 @@ type Area struct {
type (
// IdentityForCompany 公司附加信息
IdentityForCompany struct {
Kind int `json:"kind" form:"kind" binding:"required"`
Product string `json:"product" form:"product" binding:"required"` // 企业产品
Url string `json:"url" form:"url"` // 企业网站
License string `json:"license" form:"license" binding:"required"` // 营业执照

View File

@ -0,0 +1,10 @@
package entity
type Message struct {
Code int `json:"code"`
Message string `json:"message"`
}
func (this *Message) Prefix() string {
return "prefix_message"
}

View File

@ -62,8 +62,7 @@ func (m *UserInstance) GetAvatarAttribute(domain string) string {
if m.Avatar == "" {
return ""
}
compile := "^(http|https):"
if utils.ValidateCompile(m.Avatar, compile) {
if utils.ValidateCompile(m.Avatar, "^(http|https):") {
return m.Avatar
}
return domain + m.Avatar

View File

@ -6,6 +6,7 @@ import (
"SciencesServer/app/service"
"SciencesServer/config"
"SciencesServer/serve/orm"
"SciencesServer/utils"
)
func Init() {
@ -29,4 +30,8 @@ func Init() {
service.NewAuth().Register()(config.EngineInfo.Mode, orm.GetDB(), model.NewSysAuthRule().TableName())
// 开启缓存存储
service.NewCache()().Init()
// 开启Websocket
go utils.TryCatch(func() {
service.NewHub().Run()
})
}

5
app/logic/inotice.go Normal file
View File

@ -0,0 +1,5 @@
package logic
type INotice interface {
Prefix() string
}

View File

@ -4,6 +4,9 @@ type ISession interface {
SetToken(key string)
GetToken() string
GetUID() uint64
GetStringUID() string
MarshalBinary() ([]byte, error)
UnmarshalBinary(data []byte) error
}

138
app/service/websocket.go Normal file
View File

@ -0,0 +1,138 @@
package service
import (
"SciencesServer/app/logic"
"SciencesServer/serve/logger"
"encoding/json"
"github.com/gorilla/websocket"
"sync"
"time"
)
type Websocket struct {
ID string
conn *websocket.Conn
send chan []byte
time *time.Ticker
}
type Hub struct {
clients map[string]*Websocket
broadcast chan logic.INotice
emit chan *HubEmit
register chan *Websocket
unregister chan *Websocket
locker *sync.RWMutex
}
type HubEmit struct {
ID string
Msg logic.INotice
}
type Message struct {
Prefix string `json:"prefix"`
logic.INotice `json:"data"`
}
var HubMessage *Hub
func (this *Hub) Run() {
for {
select {
case client := <-this.register:
this.clients[client.ID] = client
logger.InfoF("客户端【%s】发起链接", client.ID)
//for {
// select {
// case <-client.time.C:
// fmt.Println("重置了")
// client.time.Stop()
// }
//}
case client := <-this.unregister:
this.locker.Lock()
if _, ok := this.clients[client.ID]; ok {
delete(this.clients, client.ID)
close(client.send)
}
this.locker.Unlock()
case message := <-this.broadcast:
_message, _ := json.Marshal(&Message{
Prefix: message.Prefix(),
INotice: message,
})
for _, client := range this.clients {
client.send <- _message
}
case iMsg := <-this.emit:
client, has := this.clients[iMsg.ID]
if has {
_message, _ := json.Marshal(&Message{
Prefix: iMsg.Msg.Prefix(),
INotice: iMsg.Msg,
})
client.send <- _message
}
}
}
}
func (this *Hub) EmitHandle(iMsg *HubEmit) {
this.emit <- iMsg
}
func (this *Hub) BroadcastHandle(msg logic.INotice) {
this.broadcast <- msg
}
func (this *Hub) RegisterHandle(ws *Websocket) {
this.register <- ws
}
func (this *Hub) UnregisterHandle(ws *Websocket) {
this.unregister <- ws
}
func NewHub() *Hub {
HubMessage = &Hub{
clients: make(map[string]*Websocket),
broadcast: make(chan logic.INotice),
emit: make(chan *HubEmit),
register: make(chan *Websocket),
unregister: make(chan *Websocket),
locker: new(sync.RWMutex),
}
return HubMessage
}
func (this *Websocket) Write() {
defer func() {
this.conn.Close()
}()
for {
select {
case message, ok := <-this.send:
if !ok {
return
}
logger.InfoF("发送到客户端【%s】信息%s", this.ID, string(message))
_ = this.conn.WriteMessage(websocket.TextMessage, message)
}
}
}
func (this *Websocket) Read() {
}
func NewWebsocket(id string, conn *websocket.Conn) *Websocket {
return &Websocket{
ID: id,
conn: conn,
send: make(chan []byte),
time: time.NewTicker(10 * time.Second),
}
}

View File

@ -27,7 +27,11 @@ func (this *Admin) TenantIDFormat() string {
return fmt.Sprintf("%d", this.TenantID)
}
func (this *Admin) UIDFormat() string {
func (this *Admin) GetUID() uint64 {
return this.UID
}
func (this *Admin) GetStringUID() string {
return fmt.Sprintf("%d", this.UID)
}

View File

@ -3,6 +3,7 @@ package session
import (
"SciencesServer/utils"
"encoding/json"
"fmt"
"time"
)
@ -30,8 +31,12 @@ func (this *Enterprise) GetToken() string {
return this.Token
}
func (this *Enterprise) UIDToString() string {
return utils.UintToString(this.UID)
func (this *Enterprise) GetUID() uint64 {
return this.UID
}
func (this *Enterprise) GetStringUID() string {
return fmt.Sprintf("%d", this.UID)
}
func (this *Enterprise) IsVip() bool {

1
go.mod
View File

@ -15,6 +15,7 @@ require (
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/go-redis/redis v6.15.9+incompatible
github.com/golang/protobuf v1.5.2
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/lestrrat-go/strftime v1.0.5 // indirect

2
go.sum
View File

@ -226,6 +226,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=

View File

@ -161,6 +161,11 @@ func registerAdminAPI(app *gin.Engine) {
apiPrefix + "/v1/account/login",
apiPrefix + "/v1/account/logout",
}...)))
{
_api := new(api.Websocket)
app.GET("/ws", _api.Ws(config.RedisKeyForAccountAdmin, session.NewAdmin()))
app.GET("/ws/publish", _api.Publish)
}
// Captcha 验证码
v1.GET("/captcha", new(api1.Captcha).Captcha)
// Upload 上传管理

View File

@ -68,7 +68,7 @@ func NeedPermission(skipperURL ...SkipperURL) gin.HandlerFunc {
}
pass, err := service.NewPermission(
service.WithAuthTenant(_session.TenantIDFormat()),
service.WithAuthUser(_session.UIDFormat()),
service.WithAuthUser(_session.GetStringUID()),
service.WithAuthRequest([]*service.AuthRequest{
&service.AuthRequest{
Url: c.Request.URL.Path,

View File

@ -2,6 +2,14 @@ package logger
import "github.com/sirupsen/logrus"
func Info(args ...interface{}) {
logger.Log(logrus.InfoLevel, args...)
}
func InfoF(format string, args ...interface{}) {
logger.Logf(logrus.InfoLevel, format, args...)
}
func Warn(args ...interface{}) {
logger.Log(logrus.WarnLevel, args...)
}

View File

@ -45,13 +45,13 @@ func TestValidateCompile(t *testing.T) {
//status := ValidateCompile(src, compile)
//t.Log(status)
//src := "htt2ps:2134"
//t.Log(src)
//compile := "^(http|https):"
//status := ValidateCompile(src, compile)
//t.Log(status)
src := "http://213213213213"
src := "ftp://2134"
t.Log(src)
status := ValidateUrl(src)
compile := "^(http|https):"
status := ValidateCompile(src, compile)
t.Log(status)
//src := "http://213213213213"
//t.Log(src)
//status := ValidateUrl(src)
//t.Log(status)
}