This commit is contained in:
Jiang Yong 2023-09-04 19:14:02 +08:00
parent b6e5112bb6
commit 800eefcebe
6 changed files with 142 additions and 14 deletions

View File

@ -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()

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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
}

View File

@ -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
} }