staff
This commit is contained in:
parent
b6e5112bb6
commit
800eefcebe
|
@ -8,7 +8,7 @@ import (
|
||||||
"enterprise/worker"
|
"enterprise/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main1() {
|
||||||
config.LoadServerConfig()
|
config.LoadServerConfig()
|
||||||
global.InitGlobal()
|
global.InitGlobal()
|
||||||
|
|
||||||
|
@ -19,6 +19,13 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func main() {
|
||||||
|
config.LoadServerConfig()
|
||||||
|
global.InitGlobal()
|
||||||
|
//cfg := config.GetConfig()
|
||||||
|
worker.SyncStaffSalary("")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func main2() {
|
func main2() {
|
||||||
config.LoadServerConfig()
|
config.LoadServerConfig()
|
||||||
|
|
|
@ -7,7 +7,8 @@ type StaffSalary struct {
|
||||||
BaseSalary float64
|
BaseSalary float64
|
||||||
RealSalary float64
|
RealSalary float64
|
||||||
SocialInsurence float64
|
SocialInsurence float64
|
||||||
Holiday int
|
PersonalTax float64
|
||||||
|
Holiday float64
|
||||||
CreateTime int64
|
CreateTime int64
|
||||||
UpdateTime int64
|
UpdateTime int64
|
||||||
Extra string
|
Extra string
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
butil "enterprise/base/util"
|
butil "enterprise/base/util"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/smbrave/goutil"
|
||||||
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Applyer struct {
|
type Applyer struct {
|
||||||
|
@ -25,6 +27,17 @@ type Selector struct {
|
||||||
Options []*Option `json:"options"`
|
Options []*Option `json:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Vacation struct {
|
||||||
|
Selector *Selector `json:"selector"`
|
||||||
|
Attendance struct {
|
||||||
|
DateRange struct {
|
||||||
|
NewBegin int64 `json:"new_begin"`
|
||||||
|
NewEnd int64 `json:"new_end"`
|
||||||
|
NewDuration int64 `json:"new_duration"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
} `json:"date_range"`
|
||||||
|
} `json:"attendance"`
|
||||||
|
}
|
||||||
type ApplyValue struct {
|
type ApplyValue struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Selector *Selector `json:"selector"`
|
Selector *Selector `json:"selector"`
|
||||||
|
@ -37,6 +50,7 @@ type ApplyValue struct {
|
||||||
Files []struct {
|
Files []struct {
|
||||||
FileId string `json:"file_id"`
|
FileId string `json:"file_id"`
|
||||||
} `json:"files"`
|
} `json:"files"`
|
||||||
|
Vacation *Vacation `json:"vacation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApplyContent struct {
|
type ApplyContent struct {
|
||||||
|
@ -70,6 +84,39 @@ type QyWeixinApprove struct {
|
||||||
QyWeixin
|
QyWeixin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *ApproveDetail) GetValue(title string) string {
|
||||||
|
|
||||||
|
for _, content := range d.ApplyData.Contents {
|
||||||
|
key := content.Title[0].Text
|
||||||
|
if key != title {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
if content.Control == "Selector" {
|
||||||
|
value = content.Value.Selector.Options[0].Value[0].Text
|
||||||
|
} else if content.Control == "Text" || content.Control == "Textarea" {
|
||||||
|
value = content.Value.Text
|
||||||
|
} else if content.Control == "Date" {
|
||||||
|
value = content.Value.Date.Timestamp
|
||||||
|
} else if content.Control == "Money" {
|
||||||
|
value = content.Value.NewMoney
|
||||||
|
} else if content.Control == "File" {
|
||||||
|
value = content.Value.Files[0].FileId
|
||||||
|
} else if content.Control == "Vacation" {
|
||||||
|
tp := content.Value.Vacation.Selector.Options[0].Value[0].Text
|
||||||
|
duration := cast.ToString(content.Value.Vacation.Attendance.DateRange.NewDuration)
|
||||||
|
value = tp + "," + duration
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ApproveDetail) GetUserid() string {
|
||||||
|
return d.Applyer.Userid
|
||||||
|
}
|
||||||
|
|
||||||
func NewQyWeixinApprove(corpId, secret, agent string) *QyWeixinApprove {
|
func NewQyWeixinApprove(corpId, secret, agent string) *QyWeixinApprove {
|
||||||
return &QyWeixinApprove{
|
return &QyWeixinApprove{
|
||||||
QyWeixin: QyWeixin{
|
QyWeixin: QyWeixin{
|
||||||
|
@ -94,3 +141,35 @@ func (q *QyWeixinApprove) GetDetail(spNo string) (*ApproveDetail, error) {
|
||||||
}
|
}
|
||||||
return rsp.Info, nil
|
return rsp.Info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *QyWeixinApprove) GetList(start, end int64, templateId string) ([]string, error) {
|
||||||
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovalinfo?access_token=%s", q.GetToken())
|
||||||
|
reqParam := make(map[string]interface{})
|
||||||
|
reqParam["starttime"] = cast.ToString(start)
|
||||||
|
reqParam["endtime"] = cast.ToString(end)
|
||||||
|
reqParam["new_cursor"] = ""
|
||||||
|
reqParam["size"] = 100
|
||||||
|
filters := make([]interface{}, 0)
|
||||||
|
if templateId != "" {
|
||||||
|
filters = append(filters, map[string]interface{}{
|
||||||
|
"key": "template_id",
|
||||||
|
"value": templateId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = append(filters, map[string]interface{}{
|
||||||
|
"key": "sp_status",
|
||||||
|
"value": "2",
|
||||||
|
})
|
||||||
|
reqParam["filters"] = filters
|
||||||
|
rspBody, err := butil.HttpPostJson(reqUrl, nil, []byte(goutil.EncodeJSON(reqParam)))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("httpPost error :%s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, err := q.GetResult(rspBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cast.ToStringSlice(result["sp_no_list"]), nil
|
||||||
|
}
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -4,18 +4,20 @@ go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ArtisanCloud/PowerWeChat/v3 v3.0.56
|
github.com/ArtisanCloud/PowerWeChat/v3 v3.0.56
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-co-op/gocron v1.31.0
|
github.com/go-co-op/gocron v1.31.0
|
||||||
github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9
|
github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9
|
||||||
|
github.com/gomodule/redigo v1.8.1
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||||
github.com/silenceper/wechat v1.2.6
|
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/smbrave/goutil v0.0.0-20230602040814-2643c72c2849
|
github.com/smbrave/goutil v0.0.0-20230602040814-2643c72c2849
|
||||||
github.com/spf13/cast v1.5.1
|
github.com/spf13/cast v1.5.1
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/viper v1.16.0
|
||||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.17
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.17
|
||||||
|
golang.org/x/crypto v0.9.0
|
||||||
gorm.io/driver/mysql v1.5.1
|
gorm.io/driver/mysql v1.5.1
|
||||||
gorm.io/gorm v1.25.2
|
gorm.io/gorm v1.25.2
|
||||||
)
|
)
|
||||||
|
@ -23,13 +25,11 @@ require (
|
||||||
require (
|
require (
|
||||||
github.com/ArtisanCloud/PowerLibs/v3 v3.0.12 // indirect
|
github.com/ArtisanCloud/PowerLibs/v3 v3.0.12 // indirect
|
||||||
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.6 // indirect
|
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.6 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a // indirect
|
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
@ -39,7 +39,6 @@ require (
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8 // indirect
|
github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8 // indirect
|
||||||
github.com/gomodule/redigo v1.8.1 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
@ -67,7 +66,6 @@ require (
|
||||||
go.uber.org/multierr v1.8.0 // indirect
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
go.uber.org/zap v1.21.0 // indirect
|
go.uber.org/zap v1.21.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.9.0 // indirect
|
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/sys v0.8.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.9.0 // indirect
|
||||||
|
|
|
@ -59,7 +59,11 @@ func SyncStaffInfo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalcStaffSalary(month string) {
|
func SyncStaffSalary(month string) {
|
||||||
|
if month == "" {
|
||||||
|
month = time.Now().AddDate(0, -1, 0).Format("2006-01")
|
||||||
|
}
|
||||||
|
month = strings.ReplaceAll(month, "-", "")
|
||||||
//cfg := config.GetConfig()
|
//cfg := config.GetConfig()
|
||||||
//hrAssiant := weixin.NewQyWeixinHR(cfg.QyWeixin.Corpid, cfg.QyWeixin.HrSecret, cfg.QyWeixin.HrSecret)
|
//hrAssiant := weixin.NewQyWeixinHR(cfg.QyWeixin.Corpid, cfg.QyWeixin.HrSecret, cfg.QyWeixin.HrSecret)
|
||||||
|
|
||||||
|
@ -68,7 +72,11 @@ func CalcStaffSalary(month string) {
|
||||||
log.Errorf("query staff db error :%s", err.Error())
|
log.Errorf("query staff db error :%s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
month = strings.ReplaceAll(month, "-", "")
|
holidays, err := loadHoliday(month)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("loadHoliday error :%s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
monthTime, _ := time.ParseInLocation("200601", month, time.Local)
|
monthTime, _ := time.ParseInLocation("200601", month, time.Local)
|
||||||
startDate := cast.ToInt(monthTime.Format("20060102"))
|
startDate := cast.ToInt(monthTime.Format("20060102"))
|
||||||
endDate := cast.ToInt(monthTime.AddDate(0, 1, -1).Format("20060102"))
|
endDate := cast.ToInt(monthTime.AddDate(0, 1, -1).Format("20060102"))
|
||||||
|
@ -94,9 +102,7 @@ func CalcStaffSalary(month string) {
|
||||||
discount = 0.8
|
discount = 0.8
|
||||||
}
|
}
|
||||||
socialInsurence := cast.ToFloat64(config.Get(model.StaffSalarySocialInsurence))
|
socialInsurence := cast.ToFloat64(config.Get(model.StaffSalarySocialInsurence))
|
||||||
if endDate%100 > 15 { // 15号以后的员工不缴社保
|
|
||||||
socialInsurence = 0
|
|
||||||
}
|
|
||||||
extra := make(map[string]interface{})
|
extra := make(map[string]interface{})
|
||||||
|
|
||||||
salary.BaseSalary = cast.ToFloat64(staff.Salary)
|
salary.BaseSalary = cast.ToFloat64(staff.Salary)
|
||||||
|
@ -116,6 +122,9 @@ func CalcStaffSalary(month string) {
|
||||||
if officialDate/100 == startDate/100 {
|
if officialDate/100 == startDate/100 {
|
||||||
isOfficialMonth = true
|
isOfficialMonth = true
|
||||||
}
|
}
|
||||||
|
if entryDate%100 > 15 && isEntryMonth { // 15号以后的员工不缴社保
|
||||||
|
socialInsurence = 0
|
||||||
|
}
|
||||||
|
|
||||||
if isEntryMonth {
|
if isEntryMonth {
|
||||||
expDays = endDate - entryDate + 1
|
expDays = endDate - entryDate + 1
|
||||||
|
@ -130,10 +139,11 @@ func CalcStaffSalary(month string) {
|
||||||
officialDays = endDate - startDate + 1
|
officialDays = endDate - startDate + 1
|
||||||
expDays = 0
|
expDays = 0
|
||||||
}
|
}
|
||||||
|
holiday := butil.FloatCut(holidays[staff.Username])
|
||||||
|
|
||||||
expSalary := cast.ToFloat64(staff.Salary) * discount / float64(endDate-startDate+1)
|
expSalary := cast.ToFloat64(staff.Salary) * discount / float64(endDate-startDate+1)
|
||||||
officalSalary := cast.ToFloat64(staff.Salary) / float64(endDate-startDate+1)
|
officalSalary := cast.ToFloat64(staff.Salary) / float64(endDate-startDate+1)
|
||||||
realSalary := expSalary*float64(expDays) + officalSalary*float64(officialDays) - socialInsurence
|
realSalary := expSalary*float64(expDays) + officalSalary*float64(officialDays) - holiday*officalSalary
|
||||||
|
|
||||||
extra["discount"] = discount
|
extra["discount"] = discount
|
||||||
extra["socialInsurence"] = socialInsurence
|
extra["socialInsurence"] = socialInsurence
|
||||||
|
@ -152,6 +162,7 @@ func CalcStaffSalary(month string) {
|
||||||
|
|
||||||
salary.RealSalary = butil.FloatCut(realSalary)
|
salary.RealSalary = butil.FloatCut(realSalary)
|
||||||
salary.SocialInsurence = socialInsurence
|
salary.SocialInsurence = socialInsurence
|
||||||
|
salary.Holiday = holiday
|
||||||
salary.Extra = goutil.EncodeJSONIndent(extra)
|
salary.Extra = goutil.EncodeJSONIndent(extra)
|
||||||
if salary.Id == 0 {
|
if salary.Id == 0 {
|
||||||
_, err = dao.NewStaffSalaryDao().Create(salary)
|
_, err = dao.NewStaffSalaryDao().Create(salary)
|
||||||
|
@ -164,3 +175,33 @@ func CalcStaffSalary(month string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadHoliday(month string) (map[string]float64, error) {
|
||||||
|
cfg := config.GetConfig().QyWeixin
|
||||||
|
approve := weixin.NewQyWeixinApprove(cfg.Corpid, cfg.ApproveSecret, cfg.ApproveAgent)
|
||||||
|
startTime, _ := time.ParseInLocation("200601", month, time.Local)
|
||||||
|
endTime := startTime.AddDate(0, 1, 0)
|
||||||
|
spNos, err := approve.GetList(startTime.Unix(), endTime.Unix()-1, "3WLJF6naF5jhnXvwisuPmE85wVMYcy1S1ZvYibkw")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("approve getlist error :%s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make(map[string]float64)
|
||||||
|
for _, spNo := range spNos {
|
||||||
|
detail, err := approve.GetDetail(spNo)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("approve GetDetail error :%s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userId := detail.GetUserid()
|
||||||
|
holidayType := detail.GetValue("请假类型")
|
||||||
|
fields := strings.SplitN(holidayType, ",", 2)
|
||||||
|
hours := float64(cast.ToInt64(fields[1])) / float64(3600*8)
|
||||||
|
if v, ok := result[userId]; ok {
|
||||||
|
result[userId] = v + hours
|
||||||
|
} else {
|
||||||
|
result[userId] = hours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,9 @@ func Init() error {
|
||||||
cron.Every(1).Day().At("03:00").Do(func() {
|
cron.Every(1).Day().At("03:00").Do(func() {
|
||||||
go SyncStaffInfo()
|
go SyncStaffInfo()
|
||||||
})
|
})
|
||||||
|
cron.Every(1).Month(1, 2, 3, 4, 5).At("02:00").Do(func() {
|
||||||
|
go SyncStaffSalary("")
|
||||||
|
})
|
||||||
cron.StartAsync()
|
cron.StartAsync()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue