Parcourir la source

first commit

main
yk il y a 1 an
révision
e3f606175a
49 fichiers modifiés avec 250423 ajouts et 0 suppressions
  1. +0
    -0
      .gitignore
  2. +15
    -0
      Dockerfile
  3. +0
    -0
      README.md
  4. +6
    -0
      app.yaml
  5. +8
    -0
      build_and_run.sh
  6. +896
    -0
      business/staff.go
  7. +125
    -0
      common/cache.go
  8. +203
    -0
      common/common.go
  9. +121
    -0
      common/config.go
  10. +101
    -0
      common/log.go
  11. +19
    -0
      config.json
  12. +0
    -0
      di.txt
  13. +20
    -0
      digimetastaffsync.iml
  14. +38
    -0
      go.mod
  15. +96
    -0
      go.sum
  16. BIN
      images/.DS_Store
  17. BIN
      logs/.DS_Store
  18. +9567
    -0
      logs/2024-01-29-info.log
  19. +42629
    -0
      logs/2024-01-30-info.log
  20. +56006
    -0
      logs/2024-01-31-info.log
  21. +94834
    -0
      logs/2024-02-04-info.log
  22. +39749
    -0
      logs/2024-02-26-info.log
  23. +2668
    -0
      logs/web.log
  24. +291
    -0
      main.go
  25. +48
    -0
      mydatabase/sql_base.go
  26. +147
    -0
      mydatabase/sql_staff.go
  27. BIN
      static/img/1111.png
  28. BIN
      static/img/3333.png
  29. BIN
      static/img/PCPeopleList.png
  30. BIN
      static/img/close.png
  31. BIN
      static/img/goTop.png
  32. BIN
      static/img/loginBox.png
  33. BIN
      static/img/logo.png
  34. BIN
      static/img/open.png
  35. BIN
      static/img/phoneBgc.jpg
  36. BIN
      static/img/phoneBgc_PC.jpg
  37. BIN
      static/img/registerBgc.jpg
  38. BIN
      static/img/registerBgc_PC.jpg
  39. BIN
      static/img/search.png
  40. BIN
      static/img/upload.png
  41. +2
    -0
      static/js/jquery.min.js
  42. +159
    -0
      static/js/msg-box.js
  43. +8
    -0
      static/styles.css
  44. +380
    -0
      templates/info.html
  45. +871
    -0
      templates/login-bak.html
  46. +578
    -0
      templates/login.html
  47. +402
    -0
      templates/mgr.html
  48. +396
    -0
      templates/reg.html
  49. +40
    -0
      templates/register.html

+ 0
- 0
.gitignore Voir le fichier


+ 15
- 0
Dockerfile Voir le fichier

@@ -0,0 +1,15 @@
FROM golang:latest

WORKDIR /app

COPY . .
RUN #go env -w GO111MODULE=off
RUN apt-get update && apt-get install -y tzdata
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN go build -o staff-sync


EXPOSE 18080
CMD ["./staff-sync"]

+ 0
- 0
README.md Voir le fichier


+ 6
- 0
app.yaml Voir le fichier

@@ -0,0 +1,6 @@
runtime: go120
api_version: go1

handlers:
- url: /.*
script: _go_app

+ 8
- 0
build_and_run.sh Voir le fichier

@@ -0,0 +1,8 @@
#!/bin/bash
# 重新构建docker镜像
sudo docker build -t staff-sync .

sudo mkdir -p /home/digimeta/staff-sync
# 运行生成的docker镜像
sudo docker run --add-host='oa.dfwytech.net:10.1.31.231' -v /home/digimeta/staff-sync/logs:/app/logs -v /home/digimeta/staff-sync/config.json:/app/config.json --restart=always -p 48480:18080 --name staff-sync -d staff-sync
sudo tail -f -n 500 /home/digimeta/logs/web.log

+ 896
- 0
business/staff.go Voir le fichier

@@ -0,0 +1,896 @@
package business

/**
* @Author: yk
* @Date: 2024/01/30 15:04
* @Desc: 外部接口实现,关于人员的接口
*/

import (
"bytes"
"digimetastaffsync/common"
"digimetastaffsync/mydatabase"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"sort"
"strconv"
"strings"
"time"
)

var timeStep int

func init() {
common.LoadConfig()
//初始化时间步长时间步长
timeStep = common.ConfigData.Timer
}

type FeatureResp struct {
Status int `json:"status"`
Message string `json:"message"`
Result []interface{} `json:"result"`
}

type Staff struct {
ID string `json:"id"`
Name string `json:"name"`
NickName string `json:"nickName"`
Phone string `json:"phone"`
Type int `json:"type"`
Url string `json:"Url"`
Image string `json:"image"`
}

type Req struct {
Code string `json:"code"`
StaffName string `json:"staffName"`
StaffType int `json:"staffType"`
Gender int `json:"gender"`
DevId string `json:"devId"`
FaceFeature string `json:"faceFeature"`
HireDate string `json:"hireDate"`
Birthday string `json:"birthDate"`
Phone string `json:"phone"`
StaffBase64Img string `json:"staffBase64Img"`
DelFlag int `json:"status"`
Modified string `json:"modified"`
}

func RegStaff(param Req) (*common.Response, error) {
url := common.ConfigData.PushDataUrl
common.Info(common.GenLogLine()+"reg staff url:%s\n", url)
method := "POST"

param.DevId = common.ConfigData.DevId
if len(param.StaffBase64Img) < 20000 {
param.StaffBase64Img = ""
}
fmt.Printf("code:%s, staffName:%s, staffType:%d, gender:%d, devId:%s, faceFeature:%s, hireDate:%s, birthday:%s, phone:%s, DelFlag:%d, modified:%s\n",
param.Code, param.StaffName, param.StaffType, param.Gender, param.DevId, param.FaceFeature, param.HireDate, param.Birthday, param.Phone, param.DelFlag, param.Modified)
common.Info(common.GenLogLine()+"exec reg code:%s, staffName:%s, staffType:%d, gender:%d, devId:%s, faceFeature:%s, hireDate:%s, birthday:%s, phone:%s, DelFlag:%d, modified:%s\n",
param.Code, param.StaffName, param.StaffType, param.Gender, param.DevId, param.FaceFeature, param.HireDate, param.Birthday, param.Phone, param.DelFlag, param.Modified)
fmt.Println()
jsonData, err := json.Marshal(param)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
}

req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData))

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil, err
}
req.Header.Add("Content-Type", "application/json")

client := &http.Client{}
res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil, err
}
var response *common.Response
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
fmt.Println("Error:", err)
return nil, err
}
common.Error(common.GenLogLine()+"reg result %s", string(body))
return response, nil
}

func RegOnlineStaff(param Req) error {
url := common.ConfigData.PushDataUrl
fmt.Printf("url:%s\n", url)
method := "POST"

param.DevId = common.ConfigData.DevId
fmt.Printf("====param:%v\n", param)
jsonData, err := json.Marshal(param)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
}

req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData))

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err
}
req.Header.Add("Content-Type", "application/json")

client := &http.Client{}
res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err
}
fmt.Println(string(body))
return nil
}

func HandleStaff(emp Req) error {
if emp.StaffBase64Img != "" {
//feature = FetchStaffFaceFeature(emp.StaffBase64Img)
//emp.FaceFeature = LocalFetchStaffFaceFeature(emp.StaffBase64Img)
}
var sType int
if emp.StaffType > 70 {
sType = 4
} else {
sType = 5
}
emp.StaffType = sType
var sDels = []int{5, 6, 7, 8}
if common.AryContainInt(sDels, emp.DelFlag) {
emp.DelFlag = 1
} else {
emp.DelFlag = 0
}

RegStaff(emp)
return nil
}

// Fetch2 获取员工信息,模拟获取saas的人员数据
func Fetch2() []Staff {
_token, err := common.GetToken()
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}
url := common.ConfigData.FetchDataUrl
method := "GET"

payload := strings.NewReader(``)

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}
token := fmt.Sprintf(`Bearer %s`, _token)
req.Header.Add("Authorization", token)

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}

var response *common.Response
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
fmt.Println("Error:", err)
}

var result []Staff

switch v := response.Data["items"].(type) {
case []interface{}:
for _, item := range v {
var s Staff
//fmt.Println(item)
if item.(map[string]interface{})["id"] != nil {
s.ID = (item.(map[string]interface{})["id"]).(string)
}
if item.(map[string]interface{})["name"] != nil {
s.Name = (item.(map[string]interface{})["name"]).(string)
}
if item.(map[string]interface{})["nickName"] != nil {
s.NickName = (item.(map[string]interface{})["nickName"]).(string)
}
if item.(map[string]interface{})["phone"] != nil {
s.Phone = (item.(map[string]interface{})["phone"]).(string)
}
if item.(map[string]interface{})["type"] != nil {
s.Type = (item.(map[string]interface{})["type"]).(int)
}
if item.(map[string]interface{})["resource"] != nil {
if item.(map[string]interface{})["resource"].(map[string]interface{})["url"] != nil {
s.Url = (item.(map[string]interface{})["resource"].(map[string]interface{})["url"]).(string)
}
}
result = append(result, s)
}
default:
fmt.Println("无法解析的类型")
}
return result
}

// AbleSendCode 发送验证码
func AbleSendCode(phone string) string {

url := common.ConfigData.CheckSendCodeUrl + "/" + phone
method := "GET"

payload := strings.NewReader("")

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
fmt.Println("response Body:", string(body))

return string(body)
}

func SendCode(phone string) string {

ableSend := AbleSendCode(phone)
fmt.Println("ableSend:", ableSend)
url := common.ConfigData.SendCodeUrl
method := "POST"

payload := strings.NewReader(`{
"phone":"` + phone + `"}`)

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
req.Header.Add("Content-Type", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
fmt.Println("response Body:", string(body))
var result map[string]interface{}
err = json.Unmarshal([]byte(string(body)), &result)
if err != nil {
fmt.Println("解析JSON出错:", err)
return err.Error()
}

return string(body)
}

func ValidCode(phone string, code string) (string, error) {
ableSend := AbleSendCode(phone)
fmt.Println("ableSend:", ableSend)
url := common.ConfigData.ValidCodeUrl + phone + "/" + code
method := "GET"

payload := strings.NewReader(``)

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return "", err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return "", err
}
fmt.Println("response Body:", string(body))

common.Error(common.GenLogLine()+"reg result %s", string(body))
return string(body), nil
}

func StaffInfo(staffId string) (map[string]interface{}, error) {
fmt.Println("staffId:", staffId)
url := common.ConfigData.StaffInfoUrl
method := "GET"
token, err := common.GetToken()
if err != nil {
return nil, err
}
payload := &bytes.Buffer{}
writer := multipart.NewWriter(payload)
_ = writer.WriteField("id", staffId)
err = writer.Close()
if err != nil {
fmt.Println(err)
return nil, err
}

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Println(err)
return nil, err
}
req.Header.Add("Authorization", "Bearer "+token)

req.Header.Set("Content-Type", writer.FormDataContentType())
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return nil, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return nil, err
}
fmt.Println(string(body))
var result map[string]interface{}
err = json.Unmarshal([]byte(string(body)), &result)
if err != nil {
fmt.Println("解析JSON出错:", err)
return nil, err
}
items, ok := result["data"].(map[string]interface{})["items"].([]interface{})
if !ok {
fmt.Println("无法获取items字段")
return nil, err
}

if len(items) == 0 {
return nil, errors.New("未找到员工信息")
}
firstItem := items[0].(map[string]interface{})

common.Error(common.GenLogLine()+"info result %v", firstItem)
return firstItem, nil
}

func MgrLogin(userName string, password string) string {
url := common.ConfigData.MgrLoginUrl
fmt.Println("url:", url)
enterpriseName := common.ConfigData.EnterpriseName
method := "POST"

payload := strings.NewReader(`{
"enterpriseName":"` + enterpriseName + `",
"userName":"` + userName + `",
"password":"` + password + `"}`)

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
req.Header.Add("Content-Type", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return err.Error()
}
fmt.Println("response Body:", string(body))

return string(body)
}

func PhoneLogin(phone string) (map[string]interface{}, error) {
url := common.ConfigData.PhoneLoginUrl
method := "POST"

payload := strings.NewReader(`{
"phone":"` + phone + `"}`)

client := &http.Client{}
req, _ := http.NewRequest(method, url, payload)

req.Header.Add("Content-Type", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil, err
}
fmt.Println("response Body:", string(body))
var result map[string]interface{}
err = json.Unmarshal([]byte(string(body)), &result)
if err != nil {
fmt.Println("解析JSON出错:", err)
return nil, err
}

return result, nil
}

func Fetch() []Req {
url := common.ConfigData.FetchData2Url
method := "POST"

payload := strings.NewReader("appId=dfwycustomer2u8&ygbh")

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return nil
}

var response *common.Resp
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
fmt.Println("Error:", err)
}

var result []Req
fmt.Printf("====%#v====datassss\n", response)

for _, item := range response.Data {
var s Req
//fmt.Println(item)
if item.(map[string]interface{})["workcode"] != nil {
s.Code = (item.(map[string]interface{})["workcode"]).(string)
}
if item.(map[string]interface{})["name"] != nil {
s.StaffName = (item.(map[string]interface{})["name"]).(string)
}
if item.(map[string]interface{})["mobile"] != nil {
s.Phone = (item.(map[string]interface{})["mobile"]).(string)
}
if item.(map[string]interface{})["level"] != nil {
s.StaffType, _ = strconv.Atoi((item.(map[string]interface{})["level"]).(string))
}
if item.(map[string]interface{})["birthday"] != nil {
s.Birthday = (item.(map[string]interface{})["birthday"]).(string)
}
if item.(map[string]interface{})["companystartdate"] != nil {
s.HireDate = (item.(map[string]interface{})["companystartdate"]).(string)
}
if item.(map[string]interface{})["status"] != nil {
s.DelFlag, _ = strconv.Atoi((item.(map[string]interface{})["status"]).(string))
}
if item.(map[string]interface{})["image"] != nil {
s.StaffBase64Img = (item.(map[string]interface{})["image"]).(string)
}
if item.(map[string]interface{})["modified"] != nil {
s.Modified = (item.(map[string]interface{})["modified"]).(string)
}

result = append(result, s)
}

if len(result) > 0 {
sort.Slice(result, func(i, j int) bool {
a, err := time.Parse("2006-01-02 15:04:05", result[i].Modified)
if err != nil {
common.Error(common.GenLogLine() + err.Error())
}
b, err2 := time.Parse("2006-01-02 15:04:05", result[j].Modified)
if err2 != nil {
common.Error(common.GenLogLine() + err2.Error())
}
return b.Before(a)
})

}

return result
}

func FetchStaffImage(ygbh string) string {

url := common.ConfigData.FetchData2Url
method := "POST"

payload := strings.NewReader("appId=dfwycustomer2u8&ygbh=" + fmt.Sprintf("%s", ygbh))

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return ""
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
res, _ := client.Do(req)

defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return ""
}

var response *common.Resp
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
fmt.Println("Error:", err)
}

var result string = ""

for _, item := range response.Data {
var s string
//fmt.Println(item)
if item.(map[string]interface{})["image"] != nil {
s = (item.(map[string]interface{})["image"]).(string)
}
result = s
}
return strings.Replace(result, "\n", "", -1)
}

func FetchStaffFaceFeature(base64 string) string {
url := "http://39.105.51.226:5000/cv/feature-extraction-service/1.7"
method := "POST"

err := common.SaveBase64ImageToFile(base64, "images/"+fmt.Sprintf("%d", time.Now().Unix())+"test.jpg")
fmt.Printf("%v\n", err)

payload := strings.NewReader(`{
"detect":true,
"base64Data":"` + base64 + `"}`)

client := &http.Client{}
req, err := http.NewRequest(method, url, payload)

if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return ""
}
req.Header.Add("Authorization", "Bearer ")
req.Header.Add("Content-Type", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return ""
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return ""
}
fmt.Println(string(body))

var response *FeatureResp
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
fmt.Println("Error:", err)
}

var result string
result, err = extractFeature(response.Result[0])
fmt.Printf(" success data : %+v\n", result)
return result
}

func extractFeature(data interface{}) (string, error) {
// 将 interface{} 类型转换为 map[string]interface{}
resultMap, ok := data.(map[string]interface{})
if !ok {
return "", fmt.Errorf("Invalid data format")
}
// 取出 "faces" 字段
faces, ok := resultMap["faces"].([]interface{})
if !ok || len(faces) == 0 {
return "", fmt.Errorf("No faces found")
}

// 取出 "feature" 字段
feature, ok := faces[0].(map[string]interface{})["feature"].([]interface{})
if !ok || len(feature) == 0 {
return "", fmt.Errorf("No feature found")
}

// 转换 feature 字段为 []float64 类型
var featureValues []float64
for _, value := range feature {
if floatValue, ok := value.(float64); ok {
featureValues = append(featureValues, floatValue)
} else {
return "", fmt.Errorf("Invalid feature value type")
}
}
str := common.FloatSliceToString(featureValues)
return str, nil
}

func LocalFetchStaffFaceFeature(base64 string) (string, string, error) {
url := common.ConfigData.LocalDetectUrl
method := "POST"

imgName := "static/faces/" + fmt.Sprintf("%d", time.Now().Unix()) + "test.jpg"
err := common.SaveBase64ImageToFile(base64, imgName)

payload := strings.NewReader(`{
"imgbase64":"` + base64 + `"}`)

client := &http.Client{}
req, _ := http.NewRequest(method, url, payload)

//req.Header.Add("Authorization", "Bearer ")
req.Header.Add("Content-Type", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return "", imgName, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(common.GenLogLine()+"%s", err)
common.Error(common.GenLogLine()+"%s", err)
return "", imgName, err
}
fmt.Println(string(body))

var response map[string]interface{}
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
fmt.Println("Error:", err)
return "", imgName, err
}

faces, ok := response["data"].(map[string]interface{})
if !ok {
fmt.Errorf("No faces found")
}

feature, ok1 := faces["faceData"].(map[string]interface{})["theFeature"].([]interface{})
if !ok1 || len(feature) == 0 {
return "", imgName, err
}

// 转换 feature 字段为 []float64 类型
var featureValues []float64
for _, value := range feature {
if floatValue, ok := value.(float64); ok {
featureValues = append(featureValues, floatValue)
} else {
return "", imgName, err
}
}
str := common.FloatSliceToString(featureValues)
return str, imgName, nil
}

func GetMaxId(dataList []Staff) string {
maxID := dataList[0].ID
common.Info(common.GenLogLine()+"maxID:", maxID)
for _, data := range dataList {
if data.ID > maxID {
maxID = data.ID
common.Info(common.GenLogLine()+"maxID:%s", maxID)
}
}
return maxID
}

func GetNewestModified(dataList []Req) string {
if len(dataList) == 0 {
return ""
}
maxID := dataList[0].Modified
return maxID
}

func TimerHandle() {

fmt.Printf("TimerHandle start %d\n", timeStep)
ticker := time.NewTicker(time.Second * time.Duration(timeStep))

defer ticker.Stop()
for range ticker.C { //十秒执行一次
common.Info(common.GenLogLine() + "=====执行timerHandle开始=====")
now := time.Now()
// 将时间设置为23点59分59秒
t := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 55, 0, now.Location())
common.Info(common.GenLogLine() + "--1,执行日志分割 START--")
duration := t.Sub(now)
isWithin10Seconds := duration > 0*time.Second && duration <= time.Duration(timeStep)*time.Second

fmt.Fprintln(os.Stdout, isWithin10Seconds)
if isWithin10Seconds {
common.Info(common.GenLogLine() + "--1.1执行RenameLogFile--")
common.RenameLogFile()
}
common.Info(common.GenLogLine() + "--1,执行日志分割 END--")
common.Info(common.GenLogLine() + "--2,数据同步 START--")

//获取接口数据
staffs := Fetch()
cachedTime := common.GetCache("maxTime")

if len(staffs) == 0 {
common.Info(common.GenLogLine() + "数据为空")
} else {
//获取最新更新时间
maxTime := GetNewestModified(staffs)
var t1 time.Time
var t2 time.Time
var t3 time.Time
if cachedTime == "" {
cachedTime = common.ReadFile("di.txt")
common.SetCacheWithExpire("maxTime", cachedTime, 0)
}
t1, _ = time.Parse("2006-01-02 15:04:05", cachedTime)
t2, _ = time.Parse("2006-01-02 15:04:05", maxTime)
if !t2.After(t1) {
common.Info(common.GenLogLine() + "--2.1, 数据已被同步")
} else {
common.SetCacheWithExpire("maxTime", maxTime, 0)

for _, staff := range staffs {
time.Sleep(1 * time.Second) // 暂停1秒
t3, _ = time.Parse("2006-01-02 15:04:05", staff.Modified)
if t3.After(t1) {
staff.StaffBase64Img = FetchStaffImage(staff.Code)
common.Info(common.GenLogLine()+"----%v", "" == staff.StaffBase64Img)
str := HandleStaff(staff)
common.Info(common.GenLogLine()+"--2.2注册人员 姓名:%s 编号:%s 更新时间:%s--\n", staff.StaffName, staff.Code, staff.Modified)
//str := RegOnlineStaff(staff)
ss := ""
if str == nil {
ss = "保存成功"
} else {
ss = str.Error()
}
fmt.Printf("注册人员:%v\n", ss)
common.Info(common.GenLogLine()+"--2.3注册人员:%v--\n", ss)
} else {
break
}
}
common.WriteFile("di.txt", maxTime)
common.Info(common.GenLogLine()+"--2.4,数据同步,缓存数据maxID-- :%s", common.GetCache("maxTime"))
}
}
common.Info(common.GenLogLine() + "--2,数据同步 END--")
common.Info(common.GenLogLine() + "=====执行timerHandle结束=====")
}
}

func EmpToReq(emp mydatabase.Emp) Req {
var req Req
req.StaffName = emp.Name
req.StaffType = 5 //普通员工
req.Phone = emp.Phone
req.StaffBase64Img = emp.Avatar
req.FaceFeature = emp.Features
return req
}

+ 125
- 0
common/cache.go Voir le fichier

@@ -0,0 +1,125 @@
package common

/**
* @Author:yk
* @Date: 2024/01/19
* @Desc: 缓存方法,文件读取写入方法
*/
import (
"encoding/base64"
"fmt"
"github.com/coocood/freecache"
"os"
"path/filepath"
"runtime/debug"
"strings"
"sync"
)

var (
instance *freecache.Cache
mu sync.Mutex
)

// GetInstance 返回单例缓存对象
func GetInstance() *freecache.Cache {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
cacheSize := 100 * 1024 * 1024
cache := freecache.NewCache(cacheSize)
instance = cache
}
}
return instance
}

// SetCache 设置缓存,默认过期时间为60秒
func SetCache(k string, v string) {
SetCacheWithExpire(k, v, 60)
}

// SetCacheWithExpire 设置缓存
func SetCacheWithExpire(k string, v string, t int) {
debug.SetGCPercent(20)
key := []byte(k)
val := []byte(v)
GetInstance().Set(key, val, t)
}

// GetCache 获取缓存
func GetCache(k string) string {
key := []byte(k)
got, err := GetInstance().Get(key)
if err != nil {
fmt.Printf(GenLogLine()+"%s", err)
Error(GenLogLine()+"%s", err)
return ""
} else {
return string(got)
}
}

// ReadFile 读取文件内容
func ReadFile(filePath string) string {
content, err := os.ReadFile(filePath) // 读取文件内容
if err != nil {
fmt.Printf("读取文件失败:%v\n", err)
Error("读取文件失败:%v\n", err)
return ""
}
fmt.Println(string(content))
return string(content)
}

// WriteFile 写入文件内容
func WriteFile(filePath string, content string) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(content)
if err != nil {
return err
}
return nil
}

func SaveBase64ImageToFile(base64Image string, outputFileName string) error {
// 解码 base64 编码的图片数据
imageData, err := base64.StdEncoding.DecodeString(extractBase64Data(base64Image))
if err != nil {
return fmt.Errorf("Error decoding base64 image data: %v", err)
}

if len(imageData) == 0 {
return fmt.Errorf("Decoded image data is empty")
}

// 确保目录存在
outputDir := filepath.Dir(outputFileName)
if err := os.MkdirAll(outputDir, 0755); err != nil {
return fmt.Errorf("Error creating directories: %v", err)
}

// 保存图片数据为文件
err = os.WriteFile(outputFileName, imageData, 0644)
if err != nil {
return fmt.Errorf("Error saving image data to file: %v", err)
}

fmt.Printf("Image saved to %s\n", outputFileName)
return nil
}

// extractBase64Data 从 base64 编码的图片数据中提取实际的 base64 数据部分
func extractBase64Data(base64Image string) string {
// 图片数据格式为 "data:image/png;base64,...",我们需要提取逗号后的部分
parts := strings.SplitN(base64Image, ",", 2)
if len(parts) != 2 {
return base64Image
}
return parts[1]
}

+ 203
- 0
common/common.go Voir le fichier

@@ -0,0 +1,203 @@
package common

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"runtime"
"strconv"
"strings"
"time"
)

type Response struct {
Msg string `json:"msg"`
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
Type string `json:"type"`
}

type Resp struct {
Code int `json:"code"`
Message string `json:"message"`
Data []interface{} `json:"data"`
}

type InterfaceResp struct {
Status int `json:"code"`
Err string `json:"message"`
Data interface{} `json:"data"`
}

func GenLogLine() string {
pc, _, _, ok := runtime.Caller(1)
if !ok {
return "unknown"
}

// 获取函数信息
f := runtime.FuncForPC(pc)
if f == nil {
return "unknown"
}

// 获取文件名和行号
file, line := f.FileLine(pc)

// 获取函数名
funcName := f.Name()

return fmt.Sprintf("file:%s; func:%s %d行:", TruncateLastSlash(file), TruncateLastSlash(funcName), line)
}

func FloatSliceToString(floats []float64) string {
var strSlice []string
for _, f := range floats {
strSlice = append(strSlice, strconv.FormatFloat(f, 'f', -1, 64))
}
return "[" + strings.Join(strSlice, ", ") + "]"
}

func TruncateLastSlash(inputString string) string {
lastIndex := strings.LastIndex(inputString, "/")
if lastIndex == -1 {
// 没有反斜线,返回原始字符串
return inputString
}

// 截断字符串并返回
return inputString[lastIndex+1:]
}

func GetToken() (string, error) {
url := ConfigData.FetchTokenUrl
method := "POST"
Info(GenLogLine() + "获取token")
token := GetCache("token")
if "" != token {
Info(GenLogLine() + "====get token from cache====")
return token, nil
}

payload := fmt.Sprintf(`{"deviceId":"%s"}`, ConfigData.DevId)
input := strings.NewReader(payload)
client := &http.Client{}
req, err := http.NewRequest(method, url, input)

if err != nil {
return "", fmt.Errorf("create request fail: %w", err)
}
req.Header.Add("Content-Type", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Errorf(GenLogLine()+"%s", err)
Error(GenLogLine()+"%s", err)
return "", fmt.Errorf("request fail: %w", err)
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Errorf(GenLogLine()+"%s", err)
Error(GenLogLine()+"%s", err)
return "", fmt.Errorf("handle resp fail: %w", err)
}

var response Response
err = json.Unmarshal([]byte(string(body)), &response)
if err != nil {
Error("Error:", err)
}
Info(GenLogLine()+"token:====", response.Data["access_token"])
result := response.Data["access_token"].(string)
SetCacheWithExpire("token", result, 3600)
return result, nil
}

func CovertTime(timeStr string) string {
location, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
fmt.Println("Error loading location:", err)
return ""
}

// 解析日期字符串
parsedDate, err := time.ParseInLocation("2006-01-02", timeStr, location)
if err != nil {
fmt.Println("Error parsing date:", err)
return ""
}

// 输出日期
fmt.Println("Parsed Date:", parsedDate)

// 将日期格式化为字符串
formattedDate := parsedDate.Format("2006-01-02")
fmt.Println("Formatted Date:", formattedDate)

// 传递给 Java 的字符串日期(带时区信息)
javaDateString := parsedDate.Format("2006-01-02T15:04:05Z07:00")
return javaDateString
}

func exec() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()

for range ticker.C {
// 请求接口A
respA, err := http.Get("http://localhost:8080/api/interfaceA")
if err != nil {
fmt.Println("请求接口A失败:", err)
continue
}
defer respA.Body.Close()

// 读取接口A返回的数据
dataA, err := io.ReadAll(respA.Body)
if err != nil {
fmt.Println("读取接口A返回数据失败:", err)
continue
}

// 将接口A返回的数据通过接口B发送到某个系统
respB, err := http.Post("http://localhost:8080/api/interfaceB", "application/json", bytes.NewBuffer(dataA))
if err != nil {
Error("发送数据到接口B失败:", err)
continue
}
defer respB.Body.Close()

// 检查接口B的响应状态
if respB.StatusCode == http.StatusOK {
Info(GenLogLine() + "数据发送成功")
} else {
Error("数据发送失败,状态码:", respB.StatusCode)
}
}
}

func AryContainInt(s []int, str int) bool {
found := false
for _, value := range s {
if value == str {
found = true
break
}
}
return found
}

func AryContainStr(s []string, str string) bool {
found := false
for _, value := range s {
if value == str {
found = true
break
}
}
return found
}

+ 121
- 0
common/config.go Voir le fichier

@@ -0,0 +1,121 @@
package common

import (
"encoding/json"
"os"
)

type Config struct {
FetchTokenUrl string `json:"fetchTokenUrl"`
Phone string `json:"phone"`
FetchDataUrl string `json:"fetchDataUrl"`
FetchData2Url string `json:"fetchData2Url"`
SendCodeUrl string `json:"sendCodeUrl"`
CheckSendCodeUrl string `json:"checkSendCodeUrl"`
PhoneLoginUrl string `json:"phoneLoginUrl"`
MgrLoginUrl string `json:"mgrLoginUrl"`
ValidCodeUrl string `json:"validCodeUrl"`
StaffInfoUrl string `json:"staffInfoUrl"`
EnterpriseName string `json:"enterpriseName"`
Port string `json:"port"`
PushDataUrl string `json:"pushDataUrl"`
LocalDetectUrl string `json:"localDetectUrl"`
DevId string `json:"devId"`
Timer int `json:"timer"`
Mode string `json:"mode"`
}

var ConfigData Config

func LoadConfig() interface{} {
// 读取配置文件
data, err := os.ReadFile("config.json")
if err != nil {
return nil
}

// 解析配置文件内容到全局变量中
err = json.Unmarshal(data, &ConfigData)
if err != nil {
return nil
}
return ConfigData
}

func UpdateConfig(updateValues map[string]interface{}) string {

config := ConfigData

for key, value := range updateValues {
switch key {
case "port":
config.Port = value.(string)
case "phone":
config.Phone = value.(string)
case "devId":
config.DevId = value.(string)
case "mode":
config.Mode = value.(string)
case "enterpriseName":
config.EnterpriseName = value.(string)
case "fetchTokenUrl":
config.FetchTokenUrl = value.(string)
case "fetchDataUrl":
config.FetchDataUrl = value.(string)
case "fetchData2Url":
config.FetchData2Url = value.(string)
case "pushDataUrl":
config.PushDataUrl = value.(string)
case "sendCodeUrl":
config.SendCodeUrl = value.(string)
case "checkSendCodeUrl":
config.CheckSendCodeUrl = value.(string)
case "mgrLoginUrl":
config.MgrLoginUrl = value.(string)
case "phoneLoginUrl":
config.PhoneLoginUrl = value.(string)
case "localDetectUrl":
config.LocalDetectUrl = value.(string)
case "validCodeUrl":
config.ValidCodeUrl = value.(string)
case "staffInfoUrl":
config.StaffInfoUrl = value.(string)
case "timer":
config.Timer = int(value.(float64))
default:
return "Unknown config key: " + key
}
}

if err := writeConfigToFile(&config, "config.json"); err != nil {
return "Failed to write config to file: " + err.Error()
}

return ""

}

// 从文件中读取配置
func readConfigFromFile(filename string) (*Config, error) {
fileContent, err := os.ReadFile(filename)
if err != nil {
return nil, err
}

var config Config
if err := json.Unmarshal(fileContent, &config); err != nil {
return nil, err
}

return &config, nil
}

// 将配置写入文件
func writeConfigToFile(config *Config, filename string) error {
configJSON, err := json.MarshalIndent(config, "", " ")
if err != nil {
return err
}

return os.WriteFile(filename, configJSON, 0644)
}

+ 101
- 0
common/log.go Voir le fichier

@@ -0,0 +1,101 @@
package common

import (
"io"
"log"
"os"
"time"
)

// flags and prefix
const (
flag = log.Ldate | log.Ltime | log.Lshortfile
preDebug = "[DEBUG] "
preInfo = "[INFO] "
preWarning = "[WARNING] "
preError = "[ERROR] "
)

// diff logger and output log file
var (
logFile io.Writer
debugLogger *log.Logger
infoLogger *log.Logger
warningLogger *log.Logger
errorLogger *log.Logger
defaultLogFile = "./logs/web.log"
)

func createFile() {
var err error
logFile, err = os.OpenFile(defaultLogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
logFile, err = os.OpenFile(defaultLogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("create log file err %+v", err)
}
}

debugLogger = log.New(io.MultiWriter(os.Stdout, logFile), preDebug, flag)
infoLogger = log.New(logFile, preInfo, flag)
warningLogger = log.New(logFile, preWarning, flag)
errorLogger = log.New(logFile, preError, flag)
}

// init for logger
func init() {
createFile()
}

// Debug logger
func Debug(format string, v ...interface{}) {
debugLogger.Printf(format, v...)
}

func Info(format string, v ...interface{}) {
infoLogger.Printf(format, v...)
}

func Warning(format string, v ...interface{}) {
warningLogger.Printf(format, v...)
}

func Error(format string, v ...interface{}) {
errorLogger.Printf(format, v...)
}

// set output file
func setOutputPath(path string) {
var err error
logFile, err = os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("create log file err %+v", err)
}
debugLogger = log.New(logFile, preDebug, flag)
infoLogger = log.New(logFile, preInfo, flag)
warningLogger = log.New(logFile, preWarning, flag)
errorLogger = log.New(logFile, preError, flag)
}

func SetFlags(flag int) {
debugLogger.SetFlags(flag)
infoLogger.SetFlags(flag)
warningLogger.SetFlags(flag)
errorLogger.SetFlags(flag)
}

func RenameLogFile() {
if _, err := os.Stat(defaultLogFile); os.IsNotExist(err) {
Info("文件不存在")
return
}

// 重命名文件
err := os.Rename(defaultLogFile, "./logs/"+time.Now().Format("2006-01-02")+"-info.log")
if err != nil {
Info("重命名失败:", err)
} else {
Info("重命名成功")
createFile()
}
}

+ 19
- 0
config.json Voir le fichier

@@ -0,0 +1,19 @@
{
"fetchTokenUrl": "http://127.0.0.1:8080/auth/deviceLogin",
"phone": "18910801519",
"fetchDataUrl": "http://127.0.0.1:8080/system/staff/list",
"fetchData2Url": "http://10.1.26.139/api/dizhiyuan/member/DZYAction",
"sendCodeUrl": "http://127.0.0.1:8080/system/sms/api/send-code",
"checkSendCodeUrl": "http://127.0.0.1:8080/system/staff/api/ableLogin",
"phoneLoginUrl": "http://127.0.0.1:8080/auth/phoneLogin",
"mgrLoginUrl": "http://127.0.0.1:8080/auth/mgr-login",
"validCodeUrl": "http://127.0.0.1:8080/system/staff/api/validCode",
"staffInfoUrl": "http://127.0.0.1:8080/system/staff/list",
"enterpriseName": "演示机",
"port": "18080",
"pushDataUrl": "http://127.0.0.1:8080/system/staff/api/new-staff",
"localDetectUrl": "http://192.168.10.32:8080/face/feature",
"devId": "442926c7610ed10b",
"timer": 90,
"mode": "online"
}

+ 0
- 0
di.txt Voir le fichier


+ 20
- 0
digimetastaffsync.iml Voir le fichier

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true">
<buildTags>
<option name="customFlags">
<array>
<option value="appengine" />
</array>
</option>
</buildTags>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="SonarLintModuleSettings">
<option name="uniqueId" value="03c654ca-3133-4399-af16-d5d0ba94b53f" />
</component>
</module>

+ 38
- 0
go.mod Voir le fichier

@@ -0,0 +1,38 @@
module digimetastaffsync

go 1.20

require (
github.com/coocood/freecache v1.2.4
github.com/gin-gonic/gin v1.9.1
)

require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.17.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

+ 96
- 0
go.sum Voir le fichier

@@ -0,0 +1,96 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M=
github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.3.0 h1:jX8FDLfW4ThVXctBNZ+3cIWnCSnrACDV73r76dy0aQQ=
github.com/leodido/go-urn v1.3.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.20 h1:BAZ50Ns0OFBNxdAqFhbZqdPcht1Xlb16pDCqkq1spr0=
github.com/mattn/go-sqlite3 v1.14.20/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

BIN
images/.DS_Store Voir le fichier


BIN
logs/.DS_Store Voir le fichier


+ 9567
- 0
logs/2024-01-29-info.log
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 42629
- 0
logs/2024-01-30-info.log
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 56006
- 0
logs/2024-01-31-info.log
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 94834
- 0
logs/2024-02-04-info.log
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 39749
- 0
logs/2024-02-26-info.log
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 2668
- 0
logs/web.log
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 291
- 0
main.go Voir le fichier

@@ -0,0 +1,291 @@
package main

import (
"digimetastaffsync/business"
"digimetastaffsync/common"
"digimetastaffsync/mydatabase"
"fmt"
"github.com/gin-gonic/gin"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
)

func init() {
setupLogger()
common.SetFlags(log.Ldate | log.Ltime)
common.LoadConfig()

}

func setupLogger() {
logsDir := "logs"
logFileName := "web.log"
logFilePath := filepath.Join(logsDir, logFileName)

// 检查日志文件是否存在
if _, err := os.Stat(logFilePath); os.IsNotExist(err) {
// 不存在则创建新文件
err := os.MkdirAll(logsDir, 0755)
if err != nil {
log.Fatalf("Error creating logs directory: %v", err)
}

// 创建日志文件
file, err := os.Create(logFilePath)
if err != nil {
log.Fatalf("Error creating log file: %v", err)
}
defer file.Close()

// 设置 Gin 默认的写入器
gin.DefaultWriter = io.MultiWriter(file)
} else {
// 存在则打开已有文件
file, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
log.Fatalf("Error opening log file: %v", err)
}
defer file.Close()

// 设置 Gin 默认的写入器
gin.DefaultWriter = io.MultiWriter(file)
}

// 设置 Gin 的日志输出
gin.SetMode(gin.ReleaseMode)
}

type Config struct {
Port string `json:"port"`
Phone string `json:"phone"`
Mode string `json:"mode"`
DevId string `json:"devId"`
}

func main() {

router := gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/", HomeHandler)
router.GET("/reg", RegPageHandler)
router.GET("/info", InfoPageHandler)
router.POST("/send_validation_code", SendCodeHandler)
router.POST("/valid_code", ValidCodeHandler)
router.POST("/phone_login", PhoneLoginHandler)
router.POST("/mgr_login", MgrLoginHandler)
router.GET("/mgr", MgrPageHandler)
router.GET("/test", TestHandler)
router.GET("/register", RegisterPageHandler)
router.POST("/register", RegisterHandler)
router.POST("/pass", PassHandler)
router.POST("/update", UpdateConfigHandler)

common.Info(common.GenLogLine()+"file content:%s", common.ReadFile("di.txt"))

defer mydatabase.GetDb().Close()
port := common.ConfigData.Port
if port == "" {
port = "18080"
log.Printf("Defaulting to port %s", port)
}

// 启动定时任务,用于同步人脸操作
go business.TimerHandle()

log.Printf("Listening on port %s", port)
log.Printf("Open http://localhost:%s in the browser", port)
router.Static("/static", "./static")
router.Run(fmt.Sprintf(":%s", port))

}

// UpdateConfigHandler 更新配置
func UpdateConfigHandler(c *gin.Context) {
var updateValues map[string]interface{}
if err := c.BindJSON(&updateValues); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

str := common.UpdateConfig(updateValues)
if str != "" {
c.JSON(http.StatusInternalServerError, gin.H{"message": str})
return
}
//更新配置文件变量configData
common.LoadConfig()

c.JSON(http.StatusOK, gin.H{"message": "Config updated successfully"})
}

func HomeHandler(c *gin.Context) {
t := c.Query("type")
c.HTML(http.StatusOK, "login.html", gin.H{
"type": t,
})
}

func RegPageHandler(c *gin.Context) {
c.HTML(http.StatusOK, "reg.html", nil)
}

func InfoPageHandler(c *gin.Context) {
staffId := c.Query("staffId")
res, err := business.StaffInfo(staffId)
if err != nil {
c.HTML(http.StatusOK, "info.html", gin.H{
"err": err.Error(),
})
}
fmt.Printf("res:%v", res)
c.HTML(http.StatusOK, "info.html", gin.H{
"info": res,
})
}

func PhoneLoginHandler(c *gin.Context) {
phone := c.PostForm("phone")
res, err := business.PhoneLogin(phone)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
}
c.JSON(http.StatusOK, res)
}

func MgrLoginHandler(c *gin.Context) {
phone := c.PostForm("userName")
pwd := c.PostForm("password")
str := business.MgrLogin(phone, pwd)
c.JSON(http.StatusOK, str)
}

func SendCodeHandler(c *gin.Context) {
phone := c.PostForm("phone")
str := business.SendCode(phone)
c.JSON(http.StatusOK, str)
}

func ValidCodeHandler(c *gin.Context) {
phone := c.PostForm("phone")
code := c.PostForm("code")
str, err := business.ValidCode(phone, code)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
}
c.JSON(http.StatusOK, str)
}

// RegisterPageHandler 处理注册页面请求
func RegisterPageHandler(c *gin.Context) {

c.HTML(http.StatusOK, "register.html", map[string]interface{}{"Message": ""})
}

// MgrPageHandler 处理管理页面请求
func MgrPageHandler(c *gin.Context) {
key := c.Query("key")
users, err := mydatabase.Select(key)
if err != nil {
log.Fatal(err)
}

if err != nil {
common.Error("Error marshaling JSON:%s", err)
return
}
c.HTML(http.StatusOK, "mgr.html", gin.H{
"users": users,
"key": key,
})
}

func TestHandler(c *gin.Context) {
fmt.Printf("timer: ====%d\n", common.ConfigData.Timer)
c.JSON(http.StatusOK, "request successfully")
}

// RegisterHandler 处理注册页面请求
func RegisterHandler(c *gin.Context) {
// 从表单中获取用户输入
username := c.PostForm("staffName")
phone := c.PostForm("staffPhone")
avatar := c.PostForm("avatar")

emp, _ := mydatabase.SelectOneByPhone(phone)
fmt.Println("====phone:" + phone)
fmt.Println(emp)
if emp.ID > 0 {
c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": "已存在该手机号对应的员工,请查验后重试!"})
return
}

// 在实际应用中,你可能会将用户信息保存到数据库中
avatar = strings.ReplaceAll(avatar, "data:image/jpeg;base64,", "")

features, imgName, err := business.LocalFetchStaffFaceFeature(avatar)
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": err.Error()})
return
}
mydatabase.Insert(username, 0, phone, imgName, features, 1)
// 将注册成功的消息渲染到页面
message := fmt.Sprintf("Registration successful! %s %s %s %s!", username, phone, imgName, features)
fmt.Println(message)
c.JSON(http.StatusOK, map[string]interface{}{"Message": "注册成功,请等待审核!"})
}

// PassHandler 审核通过或拒绝的处理函数
func PassHandler(c *gin.Context) {
// 从表单中获取用户输入
id := c.PostForm("id")
handleType := c.PostForm("handle")
handleTypeInt, _ := strconv.Atoi(handleType)

if handleTypeInt != 1 && handleTypeInt != 2 && handleTypeInt != 4 {
c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": "handleType参数错误!"})
return
}

idInt, _ := strconv.Atoi(id)
emp, err := mydatabase.SelectOne(idInt)

if err != nil || !(emp.ID > 0) {
c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": "指定id未查询到记录"})
return
}

if handleTypeInt == 1 {
emp.Avatar = strings.ReplaceAll(emp.Avatar, "data:image/jpeg;base64,", "")
//将员工信息提交到后台
resp, err := business.RegStaff(business.EmpToReq(emp))
if err != nil {
c.JSON(http.StatusOK, map[string]interface{}{"Message": err.Error()})
return
}
if resp != nil && resp.Code != 0 {
c.JSON(http.StatusOK, map[string]interface{}{"Message": resp.Msg})
return
}
//更新sqllite里面数据状态
mydatabase.Update(emp.ID, emp.Name, emp.Phone, emp.Avatar, 1, 1)

c.JSON(http.StatusOK, map[string]interface{}{"Message": "操作成功,注册流程结束!"})
} else if handleTypeInt == 2 {
//更新sqllite里面数据状态
mydatabase.SoftDelete(emp.ID)
c.JSON(http.StatusOK, map[string]interface{}{"Message": "操作成功"})
} else if handleTypeInt == 4 {
//删除数据
mydatabase.Delete(emp.ID)
//删除指定路径图片
if emp.Avatar != "" {
os.Remove(emp.Avatar)
}
c.JSON(http.StatusOK, map[string]interface{}{"Message": "操作成功"})
}
}

+ 48
- 0
mydatabase/sql_base.go Voir le fichier

@@ -0,0 +1,48 @@
package mydatabase

import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
"sync"
)

var (
db2 *Database
mu2 sync.Mutex
)

// Database 结构体封装了数据库连接和相关的操作方法
type Database struct {
db *sql.DB
}

// NewDatabase 创建一个新的 Database 实例
func NewDatabase(dbPath string) *Database {
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
fmt.Printf("Failed to connect to database: %v", err)
return nil
}

return &Database{db}
}

// Close 关闭数据库连接
func (d *Database) Close() error {
return d.db.Close()
}

// GetDb 返回单例缓存对象
func GetDb() *Database {
if db2 == nil {
mu2.Lock()
defer mu2.Unlock()
if db2 == nil {
cache := NewDatabase("./staffs.db")
cache.Create()
db2 = cache
}
}
return db2
}

+ 147
- 0
mydatabase/sql_staff.go Voir le fichier

@@ -0,0 +1,147 @@
package mydatabase

import (
"fmt"
"log"
"time"
)

type Emp struct {
ID int
Eno int // 员工的ID
Name string
Phone string
Avatar string
Features string
Level int
Status int
UpdateTime string
CreateTime string
}

func (d *Database) Create() error {
createTableStmt := `
CREATE TABLE IF NOT EXISTS staff (
id INTEGER PRIMARY KEY AUTOINCREMENT,
eno INTEGER,
name TEXT NOT NULL,
phone TEXT,
avatar TEXT,
level INTEGER,
status INTEGER DEFAULT 0,
features TEXT,
create_time DATETIME,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
`
_, err := d.db.Exec(createTableStmt)
if err != nil {
log.Fatal(err)
}
return err
}

func Insert(name string, eno int, phone string, avatar string, features string, level int) error {
insertStmt := "INSERT INTO staff (name,eno, phone, avatar,features, level, create_time) VALUES (?, ?, ?, ?,?, ?, ?)"
eventTime := time.Now()
// 将时间格式化为 SQLite 支持的 DATETIME 格式("2006-01-02 15:04:05")
formattedTime := eventTime.Format("2006-01-02 15:04:05")
_, err := GetDb().db.Exec(insertStmt, name, eno, phone, avatar, features, level, formattedTime)
if err != nil {
log.Fatal(err)
}
return err
}

func Update(id int, name string, phone string, avatar string, level int, status int) error {
updateStmt := "UPDATE staff SET phone = ?, name = ?, avatar = ?, level=?, status=? WHERE id = ?"
_, err := GetDb().db.Exec(updateStmt, phone, name, avatar, level, status, id)
if err != nil {
log.Fatal(err)
}
return err
}

func Delete(id int) {
deleteStmt := "DELETE FROM staff WHERE id = ?"
_, err := GetDb().db.Exec(deleteStmt, id)
if err != nil {
log.Fatal(err)
}
}

func SoftDelete(id int) error {
updateStmt := "UPDATE staff SET status=2 WHERE id = ?"
_, err := GetDb().db.Exec(updateStmt, id)
if err != nil {
log.Fatal(err)
}
return err
}

func Select(key string) ([]Emp, error) {
str := "SELECT id, eno, name, phone, avatar,features, level, status, create_time, update_time FROM staff"
if key != "" {
str += " WHERE name LIKE '%" + key + "%' or phone LIKE '%" + key + "%'"
}
rows, err := GetDb().db.Query(str + " order by status, update_time desc")
if err != nil {
log.Fatal(err)
}
defer rows.Close()

var users []Emp
for rows.Next() {
var user Emp
err := rows.Scan(&user.ID, &user.Eno, &user.Name, &user.Phone, &user.Avatar, &user.Features, &user.Level, &user.Status, &user.CreateTime, &user.UpdateTime)
if err != nil {
return nil, err
}
users = append(users, user)
}
return users, nil
}

func SelectOne(id int) (Emp, error) {
rows, err := GetDb().db.Query("SELECT id, eno, name, phone, avatar,features, level, status, create_time, update_time FROM staff where id = ? LIMIT 1", id)
if err != nil {
log.Fatal(err)
}
defer rows.Close()

var user Emp
if rows.Next() {
err := rows.Scan(&user.ID, &user.Eno, &user.Name, &user.Phone, &user.Avatar, &user.Features, &user.Level, &user.Status, &user.CreateTime, &user.UpdateTime)
if err != nil {
return user, err
}
}

if err := rows.Err(); err != nil {
return user, err
}
return user, nil
}

func SelectOneByPhone(phone string) (Emp, error) {
fmt.Printf("SelectOneByPhone phone:%s\n", phone)
rows, err := GetDb().db.Query("SELECT id, eno, name, phone, avatar,features, level, status, create_time, update_time FROM staff where phone = ? and status<>2 LIMIT 1", phone)
if err != nil {
log.Fatal(err)
}
defer rows.Close()

var user Emp

if rows.Next() {
err := rows.Scan(&user.ID, &user.Eno, &user.Name, &user.Phone, &user.Avatar, &user.Features, &user.Level, &user.Status, &user.CreateTime, &user.UpdateTime)
if err != nil {
return user, err
}
}

if err := rows.Err(); err != nil {
return user, err
}
return user, nil
}

BIN
static/img/1111.png Voir le fichier

Avant Après
Largeur: 1032  |  Hauteur: 740  |  Taille: 32 KiB

BIN
static/img/3333.png Voir le fichier

Avant Après
Largeur: 950  |  Hauteur: 680  |  Taille: 23 KiB

BIN
static/img/PCPeopleList.png Voir le fichier

Avant Après
Largeur: 1709  |  Hauteur: 1041  |  Taille: 17 KiB

BIN
static/img/close.png Voir le fichier

Avant Après
Largeur: 80  |  Hauteur: 80  |  Taille: 2.0 KiB

BIN
static/img/goTop.png Voir le fichier

Avant Après
Largeur: 65  |  Hauteur: 65  |  Taille: 2.9 KiB

BIN
static/img/loginBox.png Voir le fichier

Avant Après
Largeur: 1014  |  Hauteur: 711  |  Taille: 25 KiB

BIN
static/img/logo.png Voir le fichier

Avant Après
Largeur: 990  |  Hauteur: 1154  |  Taille: 23 KiB

BIN
static/img/open.png Voir le fichier

Avant Après
Largeur: 80  |  Hauteur: 80  |  Taille: 2.6 KiB

BIN
static/img/phoneBgc.jpg Voir le fichier

Avant Après
Largeur: 1080  |  Hauteur: 1920  |  Taille: 200 KiB

BIN
static/img/phoneBgc_PC.jpg Voir le fichier

Avant Après
Largeur: 1920  |  Hauteur: 1080  |  Taille: 334 KiB

BIN
static/img/registerBgc.jpg Voir le fichier

Avant Après
Largeur: 1080  |  Hauteur: 1920  |  Taille: 177 KiB

BIN
static/img/registerBgc_PC.jpg Voir le fichier

Avant Après
Largeur: 1920  |  Hauteur: 1080  |  Taille: 174 KiB

BIN
static/img/search.png Voir le fichier

Avant Après
Largeur: 39  |  Hauteur: 39  |  Taille: 1.5 KiB

BIN
static/img/upload.png Voir le fichier

Avant Après
Largeur: 380  |  Hauteur: 380  |  Taille: 20 KiB

+ 2
- 0
static/js/jquery.min.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 159
- 0
static/js/msg-box.js Voir le fichier

@@ -0,0 +1,159 @@
//生成Html
var GenerateHtml = function(type, title, msg) {
var _html = "";
_html += '<div id="mb_box"></div><div id="mb_con"><span id="mb_tit">' + title + '</span>';
_html += '<a id="mb_ico">x</a><div id="mb_msg" style="text-align:center;">' + msg + '</div><div id="mb_btnbox">';
if (type == "alert") {
_html += '<input id="mb_btn_ok" type="button" value="确定" />';
}
if (type == "confirm") {
_html += '<input id="mb_btn_ok" type="button" value="确定" />';
_html += '<input id="mb_btn_no" type="button" value="取消" />';
}
_html += '</div></div>';
//必须先将_html添加到body,再设置Css样式
$("body").append(_html);
//生成Css
GenerateCss();
}

//生成Css
var GenerateCss = function() {
var _widht = document.documentElement.clientWidth; //屏幕宽
var _height = document.documentElement.clientHeight; //屏幕高
var isPhonePage = 0;
if (_widht<_height) {
isPhonePage = 1;
}
$("#mb_box").css({
width: '100%',
height: '100%',
zIndex: '99999',
position: 'fixed',
filter: 'Alpha(opacity=60)',
backgroundColor: 'black',
top: '0',
left: '0',
opacity: '0.6'
});
$("#mb_con").css({
zIndex: '999999',
width: isPhonePage?'80%':'400px',
position: 'fixed',
backgroundColor: 'White',
borderRadius: '15px'
});
$("#mb_tit").css({
display: 'block',
fontSize: '14px',
color: '#444',
padding: '10px 15px',
backgroundColor: '#DDD',
borderRadius: '15px 15px 0 0',
borderBottom: '3px solid #009BFE',
fontWeight: 'bold'
});
$("#mb_msg").css({
padding: '20px',
lineHeight: '20px',
borderBottom: '1px dashed #DDD',
fontSize: '13px'
});
$("#mb_ico").css({
display: 'block',
position: 'absolute',
right: '10px',
top: '9px',
border: '1px solid Gray',
width: '18px',
height: '18px',
textAlign: 'center',
lineHeight: '16px',
cursor: 'pointer',
borderRadius: '12px',
fontFamily: '微软雅黑'
});
$("#mb_btnbox").css({
margin: '15px 0 10px 0',
textAlign: 'center'
});
$("#mb_btn_ok,#mb_btn_no").css({
width: '85px',
height: '30px',
color: 'white',
border: 'none'
});
$("#mb_btn_ok").css({
backgroundColor: '#168bbb'
});
$("#mb_btn_no").css({
backgroundColor: 'gray',
marginLeft: '20px'
});
//右上角关闭按钮hover样式
$("#mb_ico").hover(function() {
$(this).css({
backgroundColor: 'Red',
color: 'White'
});
}, function() {
$(this).css({
backgroundColor: '#DDD',
color: 'black'
});
});

console.log($(document.body).width())
console.log(_widht)
var boxWidth = $("#mb_con").width();
var boxHeight = $("#mb_con").height();
//让提示框居中
$("#mb_con").css({
top: (_height - boxHeight) / 2 + "px",
left: (_widht - boxWidth) / 2 + "px"
});
}
//确定按钮事件
var btnOk = function(callback) {
$("#mb_btn_ok").click(function() {
$("#mb_box,#mb_con").remove();
if (typeof(callback) == 'function') {
callback();
}
});
}
//取消按钮事件
var btnNo = function() {
$("#mb_btn_no,#mb_ico").click(function() {
$("#mb_box,#mb_con").remove();
});
}
var btnDel = function(callback) {
$("#mb_ico").click(function() {
$("#mb_box,#mb_con").remove();
if (typeof(callback) == 'function') {
callback();
}
});
}



$.MsgBox = {
Alert: function(title, msg) {
GenerateHtml("alert", title, msg);
btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback
btnNo();
},
AlertWithCallback: function(title, msg, callback) {
GenerateHtml("alert", title, msg);
btnOk(callback); //alert只是弹出消息,因此没必要用到回调函数callback
btnNo();
btnDel(callback)
},
Confirm: function(title, msg, callback) {
GenerateHtml("confirm", title, msg);
btnOk(callback);
btnNo();
}
}

+ 8
- 0
static/styles.css Voir le fichier

@@ -0,0 +1,8 @@
body {
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}

h1 {
color: #333;
}

+ 380
- 0
templates/info.html Voir le fichier

@@ -0,0 +1,380 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>信息页面</title>
<style>
body{
margin: 0;
}
li {
list-style-type: none;
}
@media screen and (orientation: portrait) {
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
}
.register {
background-image: url("/static/img/registerBgc.jpg") !important;
padding-top: 15vh;
}

.register_loginTitle {
width: 100vw;
font-size: 8vw;
text-align: center;
}
.register_inputBox {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5vh;
position: relative;
}
.register_phoneInput {
width: 50vw;
height: 5vh;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
margin-bottom: 2vh;
}

.register_nameInput {
width: 50vw;
height: 5vh;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
margin-bottom: 2vh;
}
.reg_button {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}
.register_loginButton {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}

.uploadImg{
width: 46vw !important;
transform: translate(0%, 0%) !important;
left: 27vw !important;
position: relative;
}
}
@media screen and (orientation: landscape) {
input:focus {
border: 0.1vw solid #000;
outline: none; /* 可选,用于去除默认的外边框样式 */
}
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc_pc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
}
.register {
background-image: url("/static/img/registerBgc_pc.jpg") !important;
}
.register_loginTitle {
width: 100vw;
display: flex;
justify-content: center;
font-size: 4vh;
text-align: center;
position: absolute;
z-index: 10;
top: 10vh;
}
.uploadImg {
width: 15vw;
}
.register_inputBox {
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
padding-top: 42vh;
}
.register_phoneInput {
width: 37vh;
height: 6vh;
border-radius: 2vh;
background-color: transparent;
font-size: 3vh;
border: 0.1vw solid #000;
margin-bottom: 5vh;
padding-left:1vw ;
}
.register_nameInput {
width: 37vh;
height: 6vh;
border-radius: 2vh;
background-color: transparent;
font-size: 3vh;
border: 0.1vw solid #000;
padding-left: 1vw ;
margin-bottom: 2vh;
}
.reg_button {
width: 16vw;
height: 3vw;
background-color: rgb(101, 161, 255);
border-radius: 10vh;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 2vw;
/* margin: 2vh 0 0 30vh; */
position: absolute;
left: 55vw;
top: 65vh;
}
.register_loginButton {
width: 8vw;
height: 2vw;
background-color: rgb(101, 161, 255);
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3vh;
position: absolute;
left: 46vw;
top: 65vh;
}

.hand:hover{
cursor: pointer;
}

.uploadImg{
width: 200px !important;
transform: translate(0%, 0%) !important;
left: 2vw !important;
position: relative;
}

}


.upload-img-box {
position: relative;
}

.upload-img-box .input-box {
display: inline-block;
width: 144px;
height: 144px;
/*border: solid black 1px;*/
}

.upload-img-box .befor-img {
display: flex;
/*width: 300px;*/
/*height: 90px;*/
/*border: solid black 1px;*/
position: absolute;
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 30;
align-items: center;
justify-content: center;
}

.upload-img-box .befor-img.hasImg {
display: none;
}

.upload-img-box .after-img {
display: inline-block;
width: 144px;
height: 144px;
border: none;
/*position: absolute;*/
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 20;
}

.after-img img, .uploadImg {
/*position: absolute;*/
width: 100%;
height: 100%;
object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/
object-position: center; /*指定对象的位置,如果不设置,则默认是center*/
background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/
background-position: center;
top: 50%;
left: 50%;
transform: translate(-50%, 0%);/*将元素移动到中心*/
border-radius: 50%;
}
</style>
</head>
<body>
<!-- 注册页面 -->
<div class="register login">
<div class="register_loginTitle ">
<div class="upload-img-box">
<div class="input-box">
<input type="file" id="img-file" hidden="hidden" name="选择图片">
</div>
<div class="befor-img">
<img class="uploadImg hand" src="/static/img/upload.png" alt="">
</div>
<div class="after-img">
<img id="img-box" src="" width="30%" height="auto">
</div>
</div>

</div>

<div class="register_inputBox">
{{ if .info }}
<input class="register_nameInput" name="staffName" type="text" placeholder="员工姓名" value="{{.info.userName}}">
<input class="register_phoneInput" name="staffPhone" type="text" placeholder="员工手机号" value="{{.info.phone}}">
{{else}}
<input class="register_nameInput" name="staffName" type="text" placeholder="员工姓名">
<input class="register_phoneInput" name="staffPhone" type="text" placeholder="员工手机号">
{{ end }}
</div>
<div class="register_loginButton hand">修改信息</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/msg-box.js"></script>
<script>
$(function(){
if ("{{.err}}"){
$.MsgBox.Alert("错误", "{{.err}}");
return;
}

$.MsgBox = {
Alert: function(title, msg) {
GenerateHtml("alert", title, msg);
btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback
btnNo();
},
Confirm: function(title, msg, callback) {
GenerateHtml("confirm", title, msg);
btnOk(callback);
btnNo();
}
}

$(".register_loginButton").on("click",function() {

var staffPhone = $(".register_phoneInput").val();
var staffName = $(".register_nameInput").val();
const regex = /^1[3-9]\d{9}$/;
if (staffPhone == "" || !regex.test(staffPhone)){
$.MsgBox.Alert("提示", "手机号为空或不合法,请检查");
return;
}
if (staffName == ""){
$.MsgBox.Alert("提示", "员工姓名为空,请检查");
return;
}

var img = $("#img-box").attr("src");
console.log(img);
$.ajax({
url: "/register",
type: "post",
data: {
staffPhone: staffPhone,
staffName: staffName,
avatar: img
},
success: function(data) {
$.MsgBox.Alert("提示", data.Message);
},
error: function(error) {
console.log(error);
$.MsgBox.Alert("提示", error.responseJSON.Message);
}
})

})

$(document).on("click", ".upload-img-box .befor-img,.upload-img-box .after-img", function (e) {
$(".input-box input").trigger("click");
})
// 使用input 的change事件,监听当文件上传的值进行改变的时候,才触发事件。
$(document).on("change", ".upload-img-box #img-file", function () {
var file = $("#img-file")[0].files[0];
console.log(file);
// file 内容包括: lastModified , lastModifiedDate , name , size , type
var formdata = new FormData();
formdata.append('file', file);

if (window.FileReader) {
var fr = new FileReader();
fr.onloadend = function (e) {
// console.log(this.result);
// document.getElementById("portrait").src = e.target.result;
$("#img-box").attr("src",e.target.result)
$(".upload-img-box .befor-img").addClass("hasImg")
};
//给FileReader对象一个读取完成的方法,
//使用readAsDataURL会返回一个url
//这个值就保存在事件对象的result里
//img通过加载这个地址,完成图片的加载
fr.readAsDataURL(file);
}

//判断图片大小
if (file.size > 3 * 1024 * 1024) {
alert("上传图片不能大于3M");
}

//判断图片数据类型
if (!/.(gif|jpg|jpeg|png|GIF|JPG|bmp)$/.test(file.name)) {

alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种");
}
})
})



</script>
</body>
</html>

+ 871
- 0
templates/login-bak.html Voir le fichier

@@ -0,0 +1,871 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin: 0;
}
li {
list-style-type: none;
}
@media screen and (orientation: portrait) {
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
}
.register {
background-image: url("/static/img/registerBgc.jpg") !important;
padding-top: 15vh;
}
.logo {
width: 15vw;
margin: 4vw 0 0 4vw;
}
.login_title {
width: 100vw;
font-size: 8vw;
text-align: center;
margin-top: 15vh;
}
.register_loginTitle {
width: 100vw;
font-size: 8vw;
text-align: center;
}
.input_box {
width: 100vw;
height: 25vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5vh;
position: relative;
}
.register_inputBox {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5vh;
position: relative;
}
.phone_input{
width: 50vw;
height: 11vw;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
position: absolute;
top: 0vw;
}
.register_phoneInput {
width: 50vw;
height: 5vh;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
margin-bottom: 2vh;
}
.code_input {
width: 50vw;
height: 11vw;
margin-top: 2vh;
border-radius: 2vw;
padding-left: 2vw;
font-size: 3vw;
background-color: transparent;
border: 1px solid #000;
position: absolute;
top: 10vw;
}
.register_codeInput {
width: 50vw;
height: 5vh;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
}
.login_button {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}
.register_loginButton {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}
.send_code {
position: absolute;
font-size: 2.5vw;
top: 18.5vw;
right: 30vw;
border-left: 2px solid;
padding-left: 2vw;
}
.employee {
display: flex;
align-items: center;
}
.administrator {
display: flex;
align-items: center;
margin-left: 5vw;
}
.choose_employee {
width: 2vw;
height: 2vw;
border-radius: 50%;
border: 2px solid #000;
margin-right: 1vw;
}
.choose_administrator {
width: 2vw;
height: 2vw;
border-radius: 50%;
border: 2px solid #000;
margin-right: 1vw;
}
.login_style {
display: flex;
justify-content: center;
align-items: center;
font-size: 2.5vw;
margin-top: 2vh;
}
.hidden {
display: none;
}
.active {
display: flex;
}
.background {
background-color: #000;
}
.password-container {
position: relative;
}
.password_toggle {
position: absolute;
width: 5vw;
top: 18vw;
right: 26vw;
}
.approval_page {
width: 100vw;
height: 100vh;
background-image: url("/static/img/registerBgc.jpg");
background-size: 100% 100%;
overflow: auto;
}
.approval_box {
max-width: 100vw;
min-height: 100vh;
background-color:rgba(237,237,237,0.8);
}
.search_people {
width: 80vw;
height: 5vh;
border-radius: 1vw;
margin: 5vh 0 0 10vw;
border: 0 solid #fff;
font-size: 3vw;
text-align: center;
}
.every_info {
height: 7vh;
margin-top: 3vh;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 3vw;
}
#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vw;
height: 10vh;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vw;
cursor: pointer;
}
.people_img {
width: 6vh;
height: 6vh;
}
.pass_nopass {
display: flex;
}
.pass {
background-color: rgb(92, 186, 45);
width: 10vw;
height: 5vw;
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
}
.no_pass {
background-color: rgb(187, 44, 45);
width: 10vw;
height: 5vw;
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
margin-left: 2vw;
}
.goTop_img {
width: 6vw;
height: 6vw;
}
}
@media screen and (orientation: landscape) {
input:focus {
border: 0.1vw solid #000;
outline: none; /* 可选,用于去除默认的外边框样式 */
}
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc_pc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
}
.PC_img {
width: 52.6vw;
height: 64.44vh;
background-image: url("/static/img/loginBox.png");
background-size: 100% 100%;
background-repeat: no-repeat;
position: absolute;
top: 18.88vh;
left: 23.7vw;
}
.register {
background-image: url("/static/img/registerBgc_pc.jpg") !important;
}
.logo {
width: 15vh;
margin: 4vh 0 0 4vh;
}
.login_title {
/* width: 100vw; */
font-size: 4vh;
text-align: center;
position: absolute;
z-index: 10;
top: 28vh;
margin-left: 58vw;
/* margin-top: 15vh;*/
}
.register_loginTitle {
width: 100vw;
display: flex;
justify-content: center;
font-size: 4vh;
text-align: center;
position: absolute;
z-index: 10;
top: 10vh;
}
.uploadImg {
width: 15vw;
}
.input_box {
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 15vh;
position: relative;
}
.register_inputBox {
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
padding-top: 42vh;
}
.phone_input{
width: 15vw;
height: 6.66vh;
border-radius: 2vh;
background-color: transparent;
font-size: 2vw;
padding-left: 2vh;
padding-right: 5vw;
border: 0.1vw solid #000;
margin-left: 25vw;
}
.register_phoneInput {
width: 37vh;
height: 6vh;
border-radius: 2vh;
background-color: transparent;
font-size: 3vh;
border: 0.1vw solid #000;
margin-bottom: 5vh;
padding-left:1vw ;
}
.register_codeInput {
width: 37vh;
height: 6vh;
border-radius: 2vh;
background-color: transparent;
font-size: 3vh;
border: 0.1vw solid #000;
padding-left: 1vw ;
}
.code_input {
width: 15vw;
height: 6.66vh;
margin-top: 2vw;
border-radius: 2vh;
padding-left: 2vh;
font-size: 1.5vw;
padding-right: 5vw;
background-color: transparent;
border: 0.1vw solid #000;
margin-left: 25vw;
}
.login_button {
width: 16vw;
height: 3vw;
background-color: rgb(101, 161, 255);
border-radius: 10vh;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 2vw;
/* margin: 2vh 0 0 30vh; */
position: absolute;
left: 55vw;
top: 65vh;
}
.register_loginButton {
width: 30vh;
height: 3vw;
background-color: rgb(101, 161, 255);
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3vh;
position: absolute;
left: 42.5vw;
top: 65vh;
}
.send_code {
position: absolute;
font-size: 1.5vh;
top: 11.5vh;
right: 27.3vw;
border-left: 0.1vw solid;
padding-left: 1vh;
display: flex;
align-items: center;
height: 3vw;
}
.employee {
display: flex;
align-items: center;
}
.administrator {
display: flex;
align-items: center;
margin-left: 3vw;
}
.hand:hover{
cursor: pointer;
}
.choose_employee {
width: 2vh;
height: 2vh;
border-radius: 50%;
border: 0.1vw solid #000;
margin-right: 1vh;
}
.choose_administrator {
width: 2vh;
height: 2vh;
border-radius: 50%;
border: 0.1vw solid #000;
margin-right: 1vh;
}
.login_style {
display: flex;
justify-content: center;
align-items: center;
font-size: 1vw;
margin-top: 2vw;
position: absolute;
left: 54.8vw;
}
.hidden {
display: none;
}
.active {
display: flex;
}
.background {
background-color: #000;
}
.password-container {
position: relative;
}
.password_toggle {
width: 3vw;
position: absolute;
font-size: 1.5vh;
top: 11.5vh;
right: 28.5vw;
padding-left: 0.5vh;
}
.approval_page {
width: 100vw;
height: 100vh;
background-image: url("/static/img/registerBgc.jpg");
background-size: 100% 100%;
overflow: auto;
}
.approval_box {
max-width: 100vw;
min-height: 100vh;
background-color:rgba(237,237,237,0.8);
}
.search_people {
width: 60vw;
height: 6vh;
border-radius: 1vh;
margin: 5vh 0 0 20vw;
border: 0 solid #fff;
font-size: 3vh;
text-align: center;
}
.every_info {
height: 7vw;
margin-top: 3vw;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 3vh;
}
#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vh;
height: 10vw;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vh;
cursor: pointer;
}
.people_img {
width: 6vw;
height: 6vw;
}
.pass_nopass {
display: flex;
}
.pass {
background-color: rgb(92, 186, 45);
width: 10vh;
height: 5vh;
border-radius: 1vh;
display: flex;
justify-content: center;
align-items: center;
}
.no_pass {
background-color: rgb(187, 44, 45);
width: 10vh;
height: 5vh;
border-radius: 1vh;
display: flex;
justify-content: center;
align-items: center;
margin-left: 2vh;
}
.goTop_img {
width: 6vh;
height: 6vh;
}
}

.list_box{
padding-inline-start: 0px;
}

.uploadImg{
width: 48% !important;
transform: translate(0%, 0%) !important;
}

.upload-img-box {
position: relative;
}

.upload-img-box .input-box {
display: inline-block;
width: 144px;
height: 144px;
/*border: solid black 1px;*/
}

.upload-img-box .befor-img {
display: flex;
/*width: 300px;*/
/*height: 90px;*/
/*border: solid black 1px;*/
position: absolute;
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 30;
align-items: center;
justify-content: center;
}

.upload-img-box .befor-img.hasImg {
display: none;
}

.upload-img-box .after-img {
display: inline-block;
width: 144px;
height: 144px;
border: none;
/*position: absolute;*/
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 20;
}

.after-img img, .uploadImg {
/*position: absolute;*/
width: 100%;
height: 100%;
object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/
object-position: center; /*指定对象的位置,如果不设置,则默认是center*/
background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/
background-position: center;
top: 50%;
left: 50%;
transform: translate(-50%, 0%);/*将元素移动到中心*/
border-radius: 50%;
}
</style>
</head>
<body>
<!-- 登录页面 -->
<div class="login">
<img class="logo" src="/static/img/logo.png" alt="">
<div class="PC_img"></div>
<div class="login_title">请 登 录</div>
<div id="employeeForm" class="input_box hidden active">
<input class="phone_input" type="text" placeholder="请输入手机号">
<input class="code_input" type="text" placeholder="请输入验证码">
<div class="send_code hand">发送验证码</div>
</div>
<div id="adminForm" class="input_box hidden password-container">
<input class="phone_input" type="text" placeholder="请输入账号">
<input id="password-input" class="code_input" type="password" placeholder="请输入密码">
<img id="password_toggle" class="password_toggle hand" src="/static/img/close.png" alt="">
</div>
<div class="login_style">
<div id="employeeBtn" class="employee hand">
<div id="choose_employee" class="choose_employee background"></div>
<div>员工登录</div>
</div>
<div id="adminBtn" class="administrator hand">
<div id="choose_administrator" class="choose_administrator"></div>
<div>管理员登录</div>
</div>
</div>
<div class="login_button hand">登 录</div>
</div>
<!-- 注册页面 -->
<div style="display: none;" class="register login">
<div class="register_loginTitle ">
<div class="upload-img-box">
<div class="input-box">
<input type="file" id="img-file" hidden="hidden" name="选择图片">
</div>
<div class="befor-img">
<img class="uploadImg hand" src="/static/img/upload.png" alt="">
</div>
<div class="after-img">
<img id="img-box" src="" width="30%" height="auto">
</div>
</div>

</div>
<div class="register_inputBox">
<input class="register_phoneInput" type="text" placeholder="员工编号">
<input class="register_codeInput" type="text" placeholder="员工姓名">
</div>
<div class="register_loginButton hand">注册人脸</div>
</div>
<!-- 管理员页面 -->
<div style="display: none;" class="approval_page">
<div id="scroll_box" class="approval_box">
<input id="search" class="search_people" type="text" placeholder="🔍 搜索">
<ul class="list_box">
<li class="every_info">
<img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div>
</li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
<li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt="">
<div class="name">张三</div>
<div class="people_code">001</div>
<div class="pass_nopass">
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
</div></li>
</ul>
</div>
<a id="scroll-to-top-btn" href="">
<img class="goTop_img" src="/static/img/goTop.png" alt="">
</a>
</div>
<script src="/static/js/jquery.min.js"></script>
<script>
$(function(){
$(".login_button").on("click",function(){
console.log("click");
$(".login").css("display","none");
// $(".approval_page").css("display","block");
$(".register").css("display","block");
})


$(document).on("click", ".upload-img-box .befor-img,.upload-img-box .after-img", function (e) {
$(".input-box input").trigger("click");
})
// 使用input 的change事件,监听当文件上传的值进行改变的时候,才触发事件。
$(document).on("change", ".upload-img-box #img-file", function () {
var file = $("#img-file")[0].files[0];
console.log(file);
// file 内容包括: lastModified , lastModifiedDate , name , size , type
var formdata = new FormData();
formdata.append('file', file);

if (window.FileReader) {
var fr = new FileReader();
fr.onloadend = function (e) {
// console.log(this.result);
// document.getElementById("portrait").src = e.target.result;
$("#img-box").attr("src",e.target.result)
$(".upload-img-box .befor-img").addClass("hasImg")
};
//给FileReader对象一个读取完成的方法,
//使用readAsDataURL会返回一个url
//这个值就保存在事件对象的result里
//img通过加载这个地址,完成图片的加载
fr.readAsDataURL(file);
}

//判断图片大小
if (file.size > 3 * 1024 * 1024) {
alert("上传图片不能大于3M");
}

//判断图片数据类型
if (!/.(gif|jpg|jpeg|png|GIF|JPG|bmp)$/.test(file.name)) {

alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种");
}
})
})
// 切换登陆页面
document.getElementById("employeeBtn").addEventListener("click", function() {
document.getElementById("employeeForm").classList.add("active");
document.getElementById("adminForm").classList.remove("active");
document.getElementById("choose_administrator").classList.remove("background");
document.getElementById("choose_employee").classList.add("background");
});
document.getElementById("adminBtn").addEventListener("click", function() {
document.getElementById("employeeForm").classList.remove("active");
document.getElementById("adminForm").classList.add("active");
document.getElementById("choose_employee").classList.remove("background");
document.getElementById("choose_administrator").classList.add("background");
});
document.getElementById("password_toggle").addEventListener("click", function() {
let passwordInput = document.getElementById("password-input")
if (passwordInput.type === 'password') {
passwordInput.type ='text';
document.getElementById("password_toggle").src = "/static/img/open.png"
} else {
passwordInput.type = "password";
document.getElementById("password_toggle").src = "/static/img/close.png"
}
});
// 点击回到顶部
var element = document.getElementById('search');

var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
var scrollToTopBtn = document.getElementById('scroll-to-top-btn');
if (entry.isIntersecting) {
// 元素进入视口,执行代码
scrollToTopBtn.style.display = 'none';
} else {
scrollToTopBtn.style.display = 'block';
}
});
});

observer.observe(element);

document.getElementById('scroll-to-top-btn').addEventListener('click', function() {
document.getElementById("scroll_box").scrollTop = 0
});



</script>
</body>
</html>

+ 578
- 0
templates/login.html Voir le fichier

@@ -0,0 +1,578 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面登录</title>
<style>
body{
margin: 0;
}
li {
list-style-type: none;
}
@media screen and (orientation: portrait) {
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
}
.register {
background-image: url("/static/img/registerBgc.jpg") !important;
padding-top: 15vh;
}
.logo {
width: 15vw;
margin: 4vw 0 0 4vw;
}
.login_title {
width: 100vw;
font-size: 8vw;
text-align: center;
margin-top: 15vh;
}
.input_box {
width: 100vw;
height: 25vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5vh;
position: relative;
}
.phone_input, .account_input{
width: 50vw;
height: 11vw;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
position: absolute;
top: 0vw;
}
.code_input, .password_input{
width: 50vw;
height: 11vw;
margin-top: 2vh;
border-radius: 2vw;
padding-left: 2vw;
font-size: 3vw;
background-color: transparent;
border: 1px solid #000;
position: absolute;
top: 10vw;
}
.login_button {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}
#sendCodeBtn {
position: absolute;
font-size: 2.5vw;
top: 18.5vw;
right: 30vw;
border-left: 2px solid;
padding-left: 2vw;
border: none;
background: transparent;
}
.employee {
display: flex;
align-items: center;
}
.administrator {
display: flex;
align-items: center;
margin-left: 5vw;
}
.choose_employee {
width: 2vw;
height: 2vw;
border-radius: 50%;
border: 2px solid #000;
margin-right: 1vw;
}
.choose_administrator {
width: 2vw;
height: 2vw;
border-radius: 50%;
border: 2px solid #000;
margin-right: 1vw;
}
.login_style {
display: flex;
justify-content: center;
align-items: center;
font-size: 2.5vw;
margin-top: 2vh;
}
.hidden {
display: none;
}
.active {
display: flex;
}
.background {
background-color: #000;
}
.password-container {
position: relative;
}
.password_toggle {
position: absolute;
width: 5vw;
top: 18vw;
right: 26vw;
}
}
@media screen and (orientation: landscape) {
input:focus {
border: 0.1vw solid #000;
outline: none; /* 可选,用于去除默认的外边框样式 */
}

.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc_pc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
}

.PC_img {
width: 52.6vw;
height: 64.44vh;
background-image: url("/static/img/loginBox.png");
background-size: 100% 100%;
background-repeat: no-repeat;
position: absolute;
top: 18.88vh;
left: 23.7vw;
}

.register {
background-image: url("/static/img/registerBgc_pc.jpg") !important;
}

.logo {
width: 15vh;
margin: 4vh 0 0 4vh;
}

.login_title {
/* width: 100vw; */
font-size: 4vh;
text-align: center;
position: absolute;
z-index: 10;
top: 28vh;
margin-left: 58vw;
/* margin-top: 15vh;*/
}

.input_box {
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 15vh;
position: relative;
}

.phone_input, .account_input {
width: 15vw;
height: 6.66vh;
border-radius: 2vh;
background-color: transparent;
font-size: 1.5vw;
padding-left: 2vh;
padding-right: 5vw;
border: 0.1vw solid #000;
margin-left: 25vw;
}


.code_input, .password_input {
width: 15vw;
height: 6.66vh;
margin-top: 2vw;
border-radius: 2vh;
padding-left: 2vh;
font-size: 1.5vw;
padding-right: 5vw;
background-color: transparent;
border: 0.1vw solid #000;
margin-left: 25vw;
}

.login_button {
width: 16vw;
height: 3vw;
background-color: rgb(101, 161, 255);
border-radius: 10vh;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 2vw;
/* margin: 2vh 0 0 30vh; */
position: absolute;
left: 55vw;
top: 65vh;
}


#sendCodeBtn {
position: absolute;
font-size: 1.5vh;
top: 11.5vh;
right: 27.3vw;
border-left: 0.1vw solid;
padding-left: 1vh;
display: flex;
align-items: center;
height: 3vw;
border: none;
background: transparent;
}

.employee {
display: flex;
align-items: center;
}

.administrator {
display: flex;
align-items: center;
margin-left: 3vw;
}

.hand:hover {
cursor: pointer;
}

.choose_employee {
width: 2vh;
height: 2vh;
border-radius: 50%;
border: 0.1vw solid #000;
margin-right: 1vh;
}

.choose_administrator {
width: 2vh;
height: 2vh;
border-radius: 50%;
border: 0.1vw solid #000;
margin-right: 1vh;
}

.login_style {
display: flex;
justify-content: center;
align-items: center;
font-size: 1vw;
margin-top: 2vw;
position: absolute;
left: 54.8vw;
}

.hidden {
display: none;
}

.active {
display: flex;
}

.background {
background-color: #000;
}

.password-container {
position: relative;
}

.password_toggle {
width: 3vw;
position: absolute;
font-size: 1.5vh;
top: 11.5vh;
right: 28.5vw;
padding-left: 0.5vh;
}

#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vh;
height: 10vw;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vh;
cursor: pointer;
}

}
button:disabled {
color: black !important;
}
</style>
</head>
<body>
<!-- 登录页面 -->
<div class="login">
<img class="logo" src="/static/img/logo.png" alt="">
<div class="PC_img"></div>
<div class="login_title">请 登 录</div>
<div id="employeeForm" class="input_box hidden active">
<input class="phone_input" type="text" placeholder="请输入手机号">
<input class="code_input" type="text" placeholder="请输入验证码">
<button id="sendCodeBtn" class="btn">发送验证码</button>
</div>
<div id="adminForm" class="input_box hidden password-container">
<input class="account_input" type="text" placeholder="请输入账号">
<input id="password_input" class="password_input" type="password" placeholder="请输入密码">
<img id="password_toggle" class="password_toggle hand" src="/static/img/close.png" alt="">
</div>
<div class="login_style">
<div id="employeeBtn" class="employee hand">
<div id="choose_employee" class="choose_employee background"></div>
<div>员工登录</div>
</div>
<div id="adminBtn" class="administrator hand">
<div id="choose_administrator" class="choose_administrator"></div>
<div>管理员登录</div>
</div>
</div>
<div class="login_button hand">登 录</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/msg-box.js"></script>
<script>
$(function(){
console.log("{{.type}}");
if ("{{.type}}" == "mgr") {
$("#adminBtn").click();
}
$.MsgBox = {
Alert: function(title, msg) {
GenerateHtml("alert", title, msg);
btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback
btnNo();
},
Confirm: function(title, msg, callback) {
GenerateHtml("confirm", title, msg);
btnOk(callback);
btnNo();
}
}


const sendCodeBtn = $('#sendCodeBtn');
let countdown = 30;
let timer;

function startCountdown() {
sendCodeBtn.attr("disabled", "disabled");
sendCodeBtn.html(`${countdown}秒`);
timer = setInterval(() => {
console.log(countdown);
countdown--;
sessionStorage.setItem('countdown',countdown);
sendCodeBtn.html(`${countdown}秒`);
if (countdown <= 0) {
clearInterval(timer);
sendCodeBtn.html('发送验证码');
sendCodeBtn.removeAttr("disabled");
countdown = 30;
}
}, 1000);
}
$("#sendCodeBtn").on("click",function(){
if (!phoneValid()){
$.MsgBox.Alert("提示", "手机号为空或不合法,请检查");
return;
}
countdown = 30;
startCountdown();
sessionStorage.setItem('countdown',countdown);
// ajax发送验证码
$.ajax({
url: '/send_validation_code',
type: 'POST',
dataType: 'json',
data: {
phone: $(".phone_input").val(),
},
success: function (response) {
console.log(response);
response = JSON.parse(response)
if (response.code == 200) {
$.MsgBox.Alert("提示", "验证码发送成功");
} else {
$.MsgBox.Alert("提示", response.msg ? response.msg : "验证码发送失败");
}
},
error: function () {
}
});
})

function initCountdown() {
const storedCountdown = sessionStorage.getItem('countdown');
if (storedCountdown) {
countdown = parseInt(storedCountdown, 10);
if (countdown > 0) {
startCountdown();
}
}
}
initCountdown();

function phoneValid() {
const regex = /^1[3-9]\d{9}$/;
var phone = $(".phone_input").val();
if (phone == "" || !regex.test(phone)){
return false;
}
return true;
}

$(".login_button").on("click",function(){
if ($("#employeeForm").hasClass("active")) {
if (!phoneValid()){
$.MsgBox.Alert("提示", "手机号为空或不合法,请检查");
return;
}
const codeRegex = /^\d{4}$/;
var code = $(".code_input").val();
if (code == "" || !codeRegex.test(code)){
$.MsgBox.Alert("提示", "验证码为4位数字,请检查");
return;
}
$.ajax({
url: '/valid_code',
type: 'POST',
dataType: 'json',
data: {
phone: $(".phone_input").val(),
code: code,
},
success: function (response) {
console.log(response);
response = JSON.parse(response)
if (response.status == 0) {
$.ajax({
url: '/phone_login',
type: 'POST',
dataType: 'json',
data: {
phone: $(".phone_input").val(),
},
success: function (response) {
console.log(response);
console.log(response.data);
console.log(response.data.staffId);
// response = JSON.parse(response)
if (response.code == 200) {
window.location.href = '/info?staffId='+response.data.staffId;

} else {
$.MsgBox.Alert("提示", response.msg);
}
},
error: function () {
}
});

} else {
$.MsgBox.Alert("提示", response.err);
}
},
error: function () {
}
});
} else {
if ($(".account_input").val() == "") {
$.MsgBox.Alert("提示", "请输入账号!");
return;
}
if ($(".password_input").val() == "") {
$.MsgBox.Alert("提示", "请输入密码!");
return;
}

$.ajax({
url: '/mgr_login',
type: 'POST',
dataType: 'json',
data: {
userName: $(".account_input").val(),
password: $(".password_input").val(),
},
success: function (response) {
console.log(response);
response = JSON.parse(response)
if (response.code == 200) {
console.log(response.data.access_token);
console.log(response.type);
sessionStorage.setItem('token',response.type);
window.location.href = '/mgr';

} else {
$.MsgBox.Alert("提示", response.msg);
}
},
error: function () {
}
});
}
})

})
// 切换登陆页面
document.getElementById("employeeBtn").addEventListener("click", function() {
document.getElementById("employeeForm").classList.add("active");
document.getElementById("adminForm").classList.remove("active");
document.getElementById("choose_administrator").classList.remove("background");
document.getElementById("choose_employee").classList.add("background");
});
document.getElementById("adminBtn").addEventListener("click", function() {
document.getElementById("employeeForm").classList.remove("active");
document.getElementById("adminForm").classList.add("active");
document.getElementById("choose_employee").classList.remove("background");
document.getElementById("choose_administrator").classList.add("background");
});
document.getElementById("password_toggle").addEventListener("click", function() {
let passwordInput = document.getElementById("password_input")
if (passwordInput.type === 'password') {
passwordInput.type ='text';
document.getElementById("password_toggle").src = "/static/img/open.png"
} else {
passwordInput.type = "password";
document.getElementById("password_toggle").src = "/static/img/close.png"
}
});




</script>
</body>
</html>

+ 402
- 0
templates/mgr.html Voir le fichier

@@ -0,0 +1,402 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理页面</title>
<style>
body{
margin: 0;
}
li {
list-style-type: none;
}
@media screen and (orientation: portrait) {
.approval_page {
width: 100vw;
height: 100vh;
background-image: url("/static/img/registerBgc.jpg");
background-size: 100% 100%;
overflow: auto;
}
.approval_box {
max-width: 100vw;
min-height: 100vh;
background-color:rgba(237,237,237,0.8);
}
.search_people {
width: 80vw;
height: 5vh;
border-radius: 1vw;
margin: 5vh 0 0 10vw;
border: 0 solid #fff;
font-size: 3vw;
text-align: center;
}
.every_info {
height: 7vh;
margin-top: 3vh;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 3vw;
}
#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vw;
height: 10vh;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vw;
cursor: pointer;
}
.people_img {
width: 6vh;
height: 6vh;
border-radius: 50%;
}
.pass_nopass {
display: flex;
}
.pass {
background-color: rgb(92, 186, 45);
width: 12vw;
height: 6vw;
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
color: white;
}
.no_pass, .del_pass {
background-color: rgb(187, 44, 45);
width: 12vw;
height: 6vw;
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
margin-left: 3vw;
color: white;
}
.goTop_img {
width: 6vw;
height: 6vw;
}

.pass_info{
width: 25vw;
}

.no_pass_info{
line-height: 3vh;
}
}
@media screen and (orientation: landscape) {
input:focus {
border: 0.1vw solid #000;
outline: none; /* 可选,用于去除默认的外边框样式 */
}
.approval_page {
width: 100vw;
height: 100vh;
background-image: url("/static/img/registerBgc.jpg");
background-size: 100% 100%;
overflow: auto;
}
.approval_box {
max-width: 100vw;
min-height: 100vh;
background-color:rgba(237,237,237,0.8);
}
.search_people {
width: 60vw;
height: 6vh;
border-radius: 1vh;
margin: 5vh 0 0 20vw;
border: 0 solid #fff;
font-size: 3vh;
text-align: center;
}
.every_info {
height: 7vw;
margin-top: 3vw;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 3vh;
}
#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vh;
height: 10vw;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vh;
cursor: pointer;
}
.people_img {
width: 6vw;
height: 6vw;
border-radius: 50%;
}
.pass_nopass {
display: flex;
}
.pass {
background-color: rgb(92, 186, 45);
width: 10vh;
height: 5vh;
border-radius: 1vh;
display: flex;
justify-content: center;
align-items: center;
color: white;
cursor: pointer;
}
.no_pass, .del_pass {
background-color: rgb(187, 44, 45);
width: 10vh;
height: 5vh;
border-radius: 1vh;
display: flex;
justify-content: center;
align-items: center;
margin-left: 2vh;
color: white;
cursor: pointer;
}
.goTop_img {
width: 6vh;
height: 6vh;
}
}

.list_box{
padding-inline-start: 0px;
}

.uploadImg{
width: 48% !important;
transform: translate(0%, 0%) !important;
}

.upload-img-box {
position: relative;
}

.upload-img-box .input-box {
display: inline-block;
width: 144px;
height: 144px;
/*border: solid black 1px;*/
}

.upload-img-box .befor-img {
display: flex;
/*width: 300px;*/
/*height: 90px;*/
/*border: solid black 1px;*/
position: absolute;
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 30;
align-items: center;
justify-content: center;
}

.upload-img-box .befor-img.hasImg {
display: none;
}

.upload-img-box .after-img {
display: inline-block;
width: 144px;
height: 144px;
border: none;
/*position: absolute;*/
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 20;
}

.after-img img, .uploadImg {
/*position: absolute;*/
width: 100%;
height: 100%;
object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/
object-position: center; /*指定对象的位置,如果不设置,则默认是center*/
background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/
background-position: center;
top: 50%;
left: 50%;
transform: translate(-50%, 0%);/*将元素移动到中心*/
border-radius: 50%;
}

.pass_info{
color: green;
}

.no_pass_info{
color: red;
}
</style>

</head>
<body>

<!-- 管理员页面 -->
<div class="approval_page" style="display: none;">
<div id="scroll_box" class="approval_box">
<input id="search" class="search_people" value="{{.key}}" type="text" placeholder="🔍 搜索">
<ul class="list_box">
{{ range $i, $v := .users }}
<li class="every_info" itemid="{{$v.ID}}">
<img class="people_img" src="{{$v.Avatar}}" alt="">
<div class="name">{{$v.Name}}</div>
<div class="people_code">{{$v.Phone}}</div>
<div class="pass_nopass">
{{ if eq $v.Status 1 }}
<div class="pass_info">通过</div>
{{ else if eq $v.Status 2 }}
<div class="no_pass_info">不通过</div>
<div class="del_pass">删除X</div>
{{ else }}
<div class="pass">通过</div>
<div class="no_pass">不通过</div>
{{ end }}
</div>
</li>
{{ end }}

</ul>
</div>
<a id="scroll-to-top-btn" href="">
<img class="goTop_img" src="/static/img/goTop.png" alt="">
</a>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/msg-box.js"></script>
<script>
console.log("9999999");
var token= sessionStorage.getItem('token');
console.log(token)
if (!token){
$.MsgBox.AlertWithCallback("提示", "此页面需要先进行登录", function (){
window.location.href = "/?type=mgr";
});
} else {
$(".approval_page").css("display","block");
}


$(function(){

var t=$("#search").val();
$("#search").val("").focus().val(t);
$('.pass').on("click",function(){
var id = $(this).parent().parent().attr('itemid');
$.ajax({
url: "/pass",
type: "post",
data: {
handle: 1,
id: id
},
success: function (data) {
$.MsgBox.AlertWithCallback("提示", data.Message, function (){
location.reload();
});
},
error: function (error) {
console.log(error);
$.MsgBox.Alert("提示", error.responseJSON.Message);
}
})
})

$('.no_pass').on("click",function(){
var id = $(this).parent().parent().attr('itemid');
$.MsgBox.Confirm("提示", "确定要否决这条注册吗?", function () {
$.ajax({
url: "/pass",
type: "post",
data: {
handle: 2,
id: id
},
success: function (data) {
location.reload();
},
error: function (error) {
console.log(error);
$.MsgBox.Alert("提示", error.responseJSON.Message);
}
})
})
})

$('.del_pass').on("click",function(){
var id = $(this).parent().parent().attr('itemid');
$.MsgBox.Confirm("提示", "确定要删除这条注册吗?", function (){
$.ajax({
url: "/pass",
type: "post",
data: {
handle: 4,
id: id
},
success: function (data) {
location.reload();
},
error: function (error) {
console.log(error);
$.MsgBox.Alert("提示", error.responseJSON.Message);
}
})
})

})

$("#search").on('change',function(e){
window.location.href = "/mgr?key="+$(this).val()
});
})

// 点击回到顶部
var element = document.getElementById('search');

var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
var scrollToTopBtn = document.getElementById('scroll-to-top-btn');
if (entry.isIntersecting) {
// 元素进入视口,执行代码
scrollToTopBtn.style.display = 'none';
} else {
scrollToTopBtn.style.display = 'block';
}
});
});

observer.observe(element);

document.getElementById('scroll-to-top-btn').addEventListener('click', function() {
document.getElementById("scroll_box").scrollTop = 0
});



</script>
</body>
</html>

+ 396
- 0
templates/reg.html Voir le fichier

@@ -0,0 +1,396 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册页面</title>
<style>
body{
margin: 0;
}
li {
list-style-type: none;
}
@media screen and (orientation: portrait) {
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
}
.register {
background-image: url("/static/img/registerBgc.jpg") !important;
padding-top: 15vh;
}

.register_loginTitle {
width: 100vw;
font-size: 8vw;
text-align: center;
}
.register_inputBox {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 5vh;
position: relative;
}
.register_phoneInput {
width: 50vw;
height: 5vh;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
margin-bottom: 2vh;
}

.register_nameInput {
width: 50vw;
height: 5vh;
border-radius: 2vw;
background-color: transparent;
font-size: 3vw;
padding-left: 2vw;
border: 1px solid #000;
margin-bottom: 2vh;
}
.reg_button {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}
.register_loginButton {
width: 40vw;
height: 5vh;
background-color: rgb(101, 161, 255);
border-radius: 10vw;
display: flex;
justify-content: center;
align-items: center;
color:#fff;
font-size: 4vw;
margin: 2vh 0 0 30vw;
}

#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vw;
height: 10vh;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vw;
cursor: pointer;
}
.uploadImg{
width: 46vw !important;
transform: translate(0%, 0%) !important;
left: 27vw !important;
position: relative;
}
}
@media screen and (orientation: landscape) {
input:focus {
border: 0.1vw solid #000;
outline: none; /* 可选,用于去除默认的外边框样式 */
}
.login {
width: 100vw;
height: 100vh;
background-image: url("/static/img/phoneBgc_pc.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
}
.register {
background-image: url("/static/img/registerBgc_pc.jpg") !important;
}
.register_loginTitle {
width: 100vw;
display: flex;
justify-content: center;
font-size: 4vh;
text-align: center;
position: absolute;
z-index: 10;
top: 10vh;
}
.uploadImg {
width: 15vw;
}
.register_inputBox {
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
padding-top: 42vh;
}
.register_phoneInput {
width: 37vh;
height: 6vh;
border-radius: 2vh;
background-color: transparent;
font-size: 3vh;
border: 0.1vw solid #000;
margin-bottom: 5vh;
padding-left:1vw ;
}
.register_nameInput {
width: 37vh;
height: 6vh;
border-radius: 2vh;
background-color: transparent;
font-size: 3vh;
border: 0.1vw solid #000;
padding-left: 1vw ;
margin-bottom: 2vh;
}
.reg_button {
width: 16vw;
height: 3vw;
background-color: rgb(101, 161, 255);
border-radius: 10vh;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 2vw;
/* margin: 2vh 0 0 30vh; */
position: absolute;
left: 55vw;
top: 65vh;
}
.register_loginButton {
width: 8vw;
height: 2vw;
background-color: rgb(101, 161, 255);
border-radius: 1vw;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3vh;
position: absolute;
left: 46vw;
top: 65vh;
}

.hand:hover{
cursor: pointer;
}

#scroll-to-top-btn {
display: none;
position: fixed;
right: 20px;
bottom: 20px;
width: 7vh;
height: 10vw;
color: #fff;
border: none;
border-radius: 5px;
font-size: 3vh;
cursor: pointer;
}

.uploadImg{
width: 200px !important;
transform: translate(0%, 0%) !important;
left: 2vw !important;
position: relative;
}

}


.upload-img-box {
position: relative;
}

.upload-img-box .input-box {
display: inline-block;
width: 144px;
height: 144px;
/*border: solid black 1px;*/
}

.upload-img-box .befor-img {
display: flex;
/*width: 300px;*/
/*height: 90px;*/
/*border: solid black 1px;*/
position: absolute;
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 30;
align-items: center;
justify-content: center;
}

.upload-img-box .befor-img.hasImg {
display: none;
}

.upload-img-box .after-img {
display: inline-block;
width: 144px;
height: 144px;
border: none;
/*position: absolute;*/
top: 0px;
left: 0px;
/*background: #ffffff;*/
z-index: 20;
}

.after-img img, .uploadImg {
/*position: absolute;*/
width: 100%;
height: 100%;
object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/
object-position: center; /*指定对象的位置,如果不设置,则默认是center*/
background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/
background-position: center;
top: 50%;
left: 50%;
transform: translate(-50%, 0%);/*将元素移动到中心*/
border-radius: 50%;
}
</style>
</head>
<body>
<!-- 注册页面 -->
<div class="register login">
<div class="register_loginTitle ">
<div class="upload-img-box">
<div class="input-box">
<input type="file" id="img-file" hidden="hidden" name="选择图片">
</div>
<div class="befor-img">
<img class="uploadImg hand" src="/static/img/upload.png" alt="">
</div>
<div class="after-img">
<img id="img-box" src="" width="30%" height="auto">
</div>
</div>

</div>
<div class="register_inputBox">
<input class="register_nameInput" name="staffName" type="text" placeholder="员工姓名">
<input class="register_phoneInput" name="staffPhone" type="text" placeholder="员工手机号">
</div>
<div class="register_loginButton hand">注册人脸</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/msg-box.js"></script>
<script>
$(function(){
$.MsgBox = {
Alert: function(title, msg) {
GenerateHtml("alert", title, msg);
btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback
btnNo();
},
Confirm: function(title, msg, callback) {
GenerateHtml("confirm", title, msg);
btnOk(callback);
btnNo();
}
}

$(".register_loginButton").on("click",function() {

var staffPhone = $(".register_phoneInput").val();
var staffName = $(".register_nameInput").val();
const regex = /^1[3-9]\d{9}$/;
if (staffPhone == "" || !regex.test(staffPhone)){
$.MsgBox.Alert("提示", "手机号为空或不合法,请检查");
return;
}
if (staffName == ""){
$.MsgBox.Alert("提示", "员工姓名为空,请检查");
return;
}

var img = $("#img-box").attr("src");
console.log(img);
$.ajax({
url: "/register",
type: "post",
data: {
staffPhone: staffPhone,
staffName: staffName,
avatar: img
},
success: function(data) {
$.MsgBox.Alert("提示", data.Message);
},
error: function(error) {
console.log(error);
$.MsgBox.Alert("提示", error.responseJSON.Message);
}
})

})

$(document).on("click", ".upload-img-box .befor-img,.upload-img-box .after-img", function (e) {
$(".input-box input").trigger("click");
})
// 使用input 的change事件,监听当文件上传的值进行改变的时候,才触发事件。
$(document).on("change", ".upload-img-box #img-file", function () {
var file = $("#img-file")[0].files[0];
console.log(file);
// file 内容包括: lastModified , lastModifiedDate , name , size , type
var formdata = new FormData();
formdata.append('file', file);

if (window.FileReader) {
var fr = new FileReader();
fr.onloadend = function (e) {
// console.log(this.result);
// document.getElementById("portrait").src = e.target.result;
$("#img-box").attr("src",e.target.result)
$(".upload-img-box .befor-img").addClass("hasImg")
};
//给FileReader对象一个读取完成的方法,
//使用readAsDataURL会返回一个url
//这个值就保存在事件对象的result里
//img通过加载这个地址,完成图片的加载
fr.readAsDataURL(file);
}

//判断图片大小
if (file.size > 3 * 1024 * 1024) {
alert("上传图片不能大于3M");
}

//判断图片数据类型
if (!/.(gif|jpg|jpeg|png|GIF|JPG|bmp)$/.test(file.name)) {

alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种");
}
})
})



</script>
</body>
</html>

+ 40
- 0
templates/register.html Voir le fichier

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
<link rel="stylesheet" href="/static/styles.css">
</head>
<body>
<h1>Register</h1>
{{if .Message}}
<p>{{.Message}}</p>
{{end}}
<form action="/register" method="post">
<label for="username">姓名:</label>
<input type="text" id="username" name="username" required><br>
{{ range $i, $v := .users }}
<label >{{ $v.Name }}</label>
{{ end }}
<label for="phone">电话:</label>
<input type="phone" id="phone" name="phone" required><br>

<label for="level">员工类型:</label>
<select>
<option value="10">员工</option>
<option value="71">领导</option>
</select><br>

<label for="level">性别:</label>
<select>
<option value="1">男</option>
<option value="2">女</option>
</select><br>

<input type="submit" value="Register">
</form>
<a href="/">Home</a>
</body>
</html>

Chargement…
Annuler
Enregistrer