609 lines
17 KiB
Go
609 lines
17 KiB
Go
package task
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"likedream-goframe/internal/application/cpdaily"
|
|
"likedream-goframe/internal/application/cpdaily/cryptoutil"
|
|
"likedream-goframe/internal/application/cpdaily/imageuploader"
|
|
"likedream-goframe/internal/application/cpdaily/task/formtype"
|
|
"likedream-goframe/internal/application/cpdaily/taskhelper"
|
|
"likedream-goframe/internal/application/cpdaily/types"
|
|
"likedream-goframe/internal/common/errors"
|
|
"likedream-goframe/internal/utils"
|
|
"maps"
|
|
"math/rand/v2"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/gogf/gf/v2/container/gvar"
|
|
"github.com/google/uuid"
|
|
"github.com/samber/oops"
|
|
"github.com/tidwall/match"
|
|
"resty.dev/v3"
|
|
)
|
|
|
|
type CpdailyCollectionTask struct {
|
|
client *resty.Client
|
|
host string
|
|
}
|
|
|
|
var _ CpdailyTask = (*CpdailyCollectionTask)(nil)
|
|
|
|
func NewCollectionTask(client *resty.Client, host string) *CpdailyCollectionTask {
|
|
return &CpdailyCollectionTask{
|
|
client: client,
|
|
host: host,
|
|
}
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) getReqUrl(path ...string) string {
|
|
appendPath := strings.Join(path, "/")
|
|
return fmt.Sprintf("%s/wec-counselor-collector-apps/stu/collector/%s", c.host, appendPath)
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) GetRecentTaskTitle(ctx context.Context) []string {
|
|
titleSet := map[string]bool{}
|
|
|
|
// 查询历史任务
|
|
historyResponse, err := c.client.R().
|
|
SetBody(map[string]any{
|
|
"pageNumber": 1,
|
|
"pageSize": 5,
|
|
}).
|
|
WithContext(ctx).
|
|
Post(c.getReqUrl("queryCollectorHistoryList"))
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.
|
|
With("host", c.host).
|
|
In("CpdailyCollectionTask.GetRecentTaskTitle").
|
|
Wrap(err)
|
|
})
|
|
|
|
historyJSON := utils.ReadGjsonResult(historyResponse.Body)
|
|
historyRows := historyJSON.Get("datas.rows").Array()
|
|
|
|
for _, row := range historyRows {
|
|
title := row.Get("subject").String()
|
|
if title != "" {
|
|
titleSet[title] = true
|
|
}
|
|
}
|
|
|
|
// 查询进行中的任务
|
|
processResponse, err := c.client.R().
|
|
SetBody(map[string]any{
|
|
"pageNumber": 1,
|
|
"pageSize": 5,
|
|
}).
|
|
WithContext(ctx).
|
|
Post(c.getReqUrl("queryCollectorProcessingList"))
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.
|
|
With("host", c.host).
|
|
In("CpdailyCollectionTask.GetRecentTaskTitle").
|
|
Wrap(err)
|
|
})
|
|
|
|
processJSON := utils.ReadGjsonResult(processResponse.Body)
|
|
processRows := processJSON.Get("datas.rows").Array()
|
|
|
|
for _, row := range processRows {
|
|
title := row.Get("subject").String()
|
|
if title != "" {
|
|
titleSet[title] = true
|
|
}
|
|
}
|
|
|
|
titles := make([]string, 0, len(titleSet))
|
|
for title := range titleSet {
|
|
titles = append(titles, title)
|
|
}
|
|
if len(titles) == 0 {
|
|
utils.OkOrPanic(errors.NewBusinessError(503, "没有查询到信息收集任务"), func(err error) error {
|
|
return oops.
|
|
With("host", c.host).
|
|
In("CpdailyCollectionTask.Count").
|
|
Wrap(err)
|
|
})
|
|
}
|
|
return titles
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) QueryFormByTitle(ctx context.Context, titleGlob string) map[string]*gvar.Var {
|
|
// 先查询进行中的任务
|
|
processResponse, err := c.client.R().
|
|
SetBody(map[string]any{
|
|
"pageNumber": 1,
|
|
"pageSize": 5,
|
|
}).
|
|
WithContext(ctx).
|
|
Post(c.getReqUrl("queryCollectorProcessingList"))
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
Wrap(err)
|
|
})
|
|
|
|
processJSON := utils.ReadJson[map[string]*gvar.Var](processResponse.Body)
|
|
processDatas := processJSON["datas"].MapStrVar()
|
|
processRows := processDatas["rows"].Array()
|
|
|
|
var matchTask map[string]*gvar.Var
|
|
|
|
// 在进行中的任务中查找匹配项
|
|
matchTask = c.findMatchTask(processRows, titleGlob)
|
|
|
|
// 如果没找到,查询历史任务
|
|
if matchTask == nil {
|
|
historyResponse, err := c.client.R().
|
|
SetBody(map[string]any{
|
|
"pageNumber": 1,
|
|
"pageSize": 5,
|
|
}).
|
|
WithContext(ctx).
|
|
Post(c.getReqUrl("queryCollectorHistoryList"))
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
Wrap(err)
|
|
})
|
|
|
|
historyJSON := utils.ReadJson[map[string]*gvar.Var](historyResponse.Body)
|
|
historyDatas := historyJSON["datas"].MapStrVar()
|
|
historyRows := historyDatas["rows"].Array()
|
|
|
|
matchTask = c.findMatchTask(historyRows, titleGlob)
|
|
}
|
|
|
|
if matchTask == nil {
|
|
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("未找到表单信息: %s", titleGlob)), func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
Wrap(err)
|
|
})
|
|
}
|
|
|
|
// 构造任务信息
|
|
taskInfo := map[string]*gvar.Var{
|
|
"wid": matchTask["wid"],
|
|
"formWid": matchTask["formWid"],
|
|
"instanceWid": gvar.New(fmt.Sprintf("%d", matchTask["instanceWid"].Int())),
|
|
"subject": matchTask["subject"],
|
|
}
|
|
|
|
// 获取详情
|
|
detailResponse, err := c.client.R().
|
|
SetBody(map[string]*gvar.Var{
|
|
"collectorWid": taskInfo["wid"],
|
|
"instanceWid": taskInfo["instanceWid"],
|
|
}).
|
|
WithContext(ctx).
|
|
Post(c.getReqUrl("detailCollector"))
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
With("titleGlob", titleGlob).
|
|
Wrap(err)
|
|
})
|
|
|
|
detailJSON := utils.ReadJson[map[string]*gvar.Var](detailResponse.Body)
|
|
detailDatas := detailJSON["datas"].MapStrVar()
|
|
|
|
if len(detailDatas) == 0 {
|
|
utils.OkOrPanic(errors.NewBusinessError(503, "未找到表单数据"), func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
With("titleGlob", titleGlob).
|
|
Wrap(err)
|
|
})
|
|
}
|
|
|
|
collector := detailDatas["collector"].MapStrVar()
|
|
taskInfo["schoolTaskWid"] = collector["schoolTaskWid"]
|
|
|
|
// 获取任务表单
|
|
formResponse, err := c.client.R().
|
|
SetBody(map[string]*gvar.Var{
|
|
"pageSize": gvar.New(9999),
|
|
"pageNumber": gvar.New(1),
|
|
"instanceWid": taskInfo["instanceWid"],
|
|
"formWid": taskInfo["formWid"],
|
|
"collectorWid": taskInfo["wid"],
|
|
}).
|
|
WithContext(ctx).
|
|
Post(c.getReqUrl("getFormFields"))
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
With("titleGlob", titleGlob).
|
|
Wrap(err)
|
|
})
|
|
|
|
formJSON := utils.ReadJson[map[string]*gvar.Var](formResponse.Body)
|
|
formDatas := formJSON["datas"].MapStrVar()
|
|
|
|
if len(formDatas) == 0 {
|
|
utils.OkOrPanic(errors.NewBusinessError(503, "未找到表单数据"), func(err error) error {
|
|
return oops.With("host", c.host).
|
|
In("CpdailyCollectionTask.QueryFormByTitle").
|
|
With("titleGlob", titleGlob).
|
|
Wrap(err)
|
|
})
|
|
}
|
|
|
|
rows := formDatas["rows"].Array()
|
|
|
|
result := make(map[string]*gvar.Var)
|
|
result["taskInfo"] = gvar.New(taskInfo)
|
|
result["rows"] = gvar.New(rows)
|
|
result["realTitle"] = taskInfo["subject"]
|
|
|
|
return result
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) QueryFormByYearMonth(ctx context.Context, titleGlob string, year, month string) map[string]*gvar.Var {
|
|
// 查寝任务不支持按年月查询,返回空结果
|
|
return make(map[string]*gvar.Var)
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) ParseFormJson(ctx context.Context, rawJson map[string]*gvar.Var) []*formtype.CpTaskFormItem {
|
|
rows := rawJson["rows"].Array()
|
|
|
|
if len(rows) == 0 {
|
|
utils.OkOrPanic(errors.NewBusinessError(503, "任务表单为空"), oops.WithContext(ctx).Wrap)
|
|
}
|
|
|
|
var list []*formtype.CpTaskFormItem
|
|
|
|
// 经纬度
|
|
dto := &formtype.CpTaskFormItem{
|
|
Title: "经度",
|
|
ColName: "lon",
|
|
Type: formtype.ItemTypeInput,
|
|
Required: true,
|
|
}
|
|
list = append(list, dto)
|
|
|
|
dto = &formtype.CpTaskFormItem{
|
|
Title: "纬度",
|
|
ColName: "lat",
|
|
Type: formtype.ItemTypeInput,
|
|
Required: true,
|
|
}
|
|
list = append(list, dto)
|
|
|
|
dto = &formtype.CpTaskFormItem{
|
|
Title: "签到具体地址",
|
|
ColName: "address",
|
|
Type: formtype.ItemTypeInput,
|
|
Required: true,
|
|
}
|
|
list = append(list, dto)
|
|
|
|
var current uint = 1
|
|
var optionShowLogic []string
|
|
|
|
// 处理表单字段
|
|
for _, row := range rows {
|
|
rowObj := row.(map[string]*gvar.Var)
|
|
title := rowObj["title"].String()
|
|
isRequired := rowObj["isRequired"].Bool()
|
|
sort := rowObj["sort"].Uint()
|
|
logicWid := rowObj["logicWid"].String()
|
|
fieldType := rowObj["fieldType"].String()
|
|
|
|
dto = &formtype.CpTaskFormItem{
|
|
Title: title,
|
|
ColName: title,
|
|
Required: isRequired,
|
|
Sort: sort,
|
|
LogicWid: logicWid,
|
|
Number: current,
|
|
}
|
|
|
|
// 根据字段类型设置类型
|
|
if c.isTextType(fieldType) {
|
|
dto.Type = formtype.ItemTypeInput
|
|
} else if fieldType == "2" {
|
|
dto.Type = formtype.ItemTypeSingle
|
|
} else if fieldType == "3" {
|
|
dto.Type = formtype.ItemTypeMulti
|
|
} else if c.isPicType(fieldType) {
|
|
dto.Type = formtype.ItemTypeImage
|
|
}
|
|
|
|
// 处理选项
|
|
fieldItems := rowObj["fieldItems"].Array()
|
|
if len(fieldItems) > 0 {
|
|
var options []formtype.CpTaskFormItemOption
|
|
for _, item := range fieldItems {
|
|
itemObj := item.(map[string]*gvar.Var)
|
|
option := formtype.CpTaskFormItemOption{
|
|
Content: itemObj["content"].String(),
|
|
IsOtherItems: itemObj["isOtherItems"].Bool(),
|
|
}
|
|
|
|
// 处理显示逻辑
|
|
showLogic := itemObj["showLogic"].String()
|
|
if showLogic != "" {
|
|
option.ShowLogic = strings.Split(showLogic, ",")
|
|
optionShowLogic = append(optionShowLogic, option.ShowLogic...)
|
|
}
|
|
|
|
options = append(options, option)
|
|
}
|
|
dto.Options = options
|
|
}
|
|
|
|
list = append(list, dto)
|
|
current++
|
|
}
|
|
|
|
// 设置显示状态
|
|
for key, item := range list {
|
|
list[key].Show = item.LogicWid == "" || !c.contains(optionShowLogic, item.LogicWid)
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, username string) map[string]*gvar.Var {
|
|
taskInfo := form["taskInfo"].MapStrVar()
|
|
rows := form["rows"].Array()
|
|
filledForm := make([]map[string]*gvar.Var, 0)
|
|
|
|
// 获取数字列表
|
|
var numbers []uint
|
|
for _, v := range paramList {
|
|
if v.Number > 0 {
|
|
numbers = append(numbers, v.Number)
|
|
}
|
|
}
|
|
|
|
// 填充表单问题
|
|
for i, row := range rows {
|
|
rowObj := row.(map[string]*gvar.Var)
|
|
newRowObj := make(map[string]*gvar.Var)
|
|
|
|
// 复制原始数据
|
|
for k, v := range rowObj {
|
|
newRowObj[k] = v
|
|
}
|
|
|
|
newRowObj["formType"] = gvar.New("0")
|
|
newRowObj["sortNum"] = gvar.New(fmt.Sprintf("%d", rowObj["sort"].Int()))
|
|
|
|
// 判断当前题目是否需要填写
|
|
if !c.containsUint(numbers, uint(i+1)) {
|
|
newRowObj["show"] = gvar.New(false)
|
|
newRowObj["value"] = gvar.New("")
|
|
delete(newRowObj, "fieldItems")
|
|
filledForm = append(filledForm, newRowObj)
|
|
continue
|
|
}
|
|
|
|
// 找到对应的参数
|
|
var param *formtype.CpTaskFormItem
|
|
for _, v := range paramList {
|
|
if v.Number == uint(i+1) {
|
|
param = v
|
|
break
|
|
}
|
|
}
|
|
|
|
if param == nil {
|
|
continue
|
|
}
|
|
|
|
questionTitle := rowObj["title"].String()
|
|
if !strings.Contains(questionTitle, param.Title) {
|
|
utils.OkOrPanic(errors.NewBusinessError(500, fmt.Sprintf("第[%d]项表单标题不匹配,请重新填写。目标标题:%s,实际标题:%s", i+1, param.Title, questionTitle)), oops.WithContext(ctx).Wrap)
|
|
}
|
|
|
|
newRowObj["show"] = gvar.New(true)
|
|
fieldType := rowObj["fieldType"].String()
|
|
|
|
// 根据字段类型填充值
|
|
if c.isTextType(fieldType) {
|
|
newRowObj["value"] = gvar.New(param.Content)
|
|
} else if fieldType == "2" {
|
|
// 单选
|
|
fieldItems := rowObj["fieldItems"].Array()
|
|
var selectOption map[string]*gvar.Var
|
|
for _, item := range fieldItems {
|
|
itemObj := item.(map[string]*gvar.Var)
|
|
if itemObj["content"].String() == param.Content {
|
|
selectOption = make(map[string]*gvar.Var)
|
|
maps.Copy(selectOption, itemObj)
|
|
selectOption["isSelected"] = gvar.New(1)
|
|
break
|
|
}
|
|
}
|
|
if selectOption != nil {
|
|
newRowObj["fieldItems"] = gvar.New([]map[string]*gvar.Var{selectOption})
|
|
newRowObj["value"] = gvar.New(selectOption["itemWid"].String())
|
|
}
|
|
} else if fieldType == "3" {
|
|
// 多选
|
|
fieldItems := rowObj["fieldItems"].Array()
|
|
var selectOptions []map[string]*gvar.Var
|
|
for _, item := range fieldItems {
|
|
itemObj := item.(map[string]*gvar.Var)
|
|
if itemObj["content"].String() == param.Content {
|
|
itemCopy := make(map[string]*gvar.Var)
|
|
for k, v := range itemObj {
|
|
itemCopy[k] = v
|
|
}
|
|
itemCopy["isSelected"] = gvar.New(1)
|
|
selectOptions = append(selectOptions, itemCopy)
|
|
}
|
|
}
|
|
if len(selectOptions) > 0 {
|
|
newRowObj["fieldItems"] = gvar.New(selectOptions)
|
|
var values []string
|
|
for _, option := range selectOptions {
|
|
values = append(values, option["itemWid"].String())
|
|
}
|
|
newRowObj["value"] = gvar.New(strings.Join(values, ","))
|
|
}
|
|
} else if c.isPicType(fieldType) {
|
|
// 图片类型
|
|
if param.Content == "" {
|
|
utils.OkOrPanic(errors.NewBusinessError(400, "查寝需要图片,但是当前图片为空"), oops.WithContext(ctx).Wrap)
|
|
}
|
|
|
|
imageUuidSeq := strings.Split(param.Content, ",")
|
|
var imageUuids []string
|
|
for _, imageUuid := range imageUuidSeq {
|
|
imageUuid = strings.TrimSpace(imageUuid)
|
|
if imageUuid == "" {
|
|
continue
|
|
}
|
|
imageUuids = append(imageUuids, imageUuid)
|
|
}
|
|
|
|
if len(imageUuids) == 0 {
|
|
utils.OkOrPanic(errors.NewBusinessError(400, "查寝照片未上传"), oops.WithContext(ctx).Wrap)
|
|
}
|
|
|
|
// 随机选择一张照片
|
|
imageUuid := imageUuids[rand.IntN(len(imageUuids))]
|
|
imageId, e := uuid.Parse(imageUuid)
|
|
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
|
|
|
|
previewUrl := imageuploader.UploadImageFromStorage(ctx, c.host, imageId, types.TaskTypeCollection, c.client)
|
|
newRowObj["value"] = gvar.New(previewUrl)
|
|
} else {
|
|
utils.OkOrPanic(errors.NewBusinessError(500, "出现未知类型表单,请反馈表单类型:"+fieldType), oops.WithContext(ctx).Wrap)
|
|
}
|
|
|
|
filledForm = append(filledForm, newRowObj)
|
|
}
|
|
|
|
// 填充必要字段
|
|
resultForm := map[string]*gvar.Var{
|
|
"form": gvar.New(filledForm),
|
|
"formWid": taskInfo["formWid"],
|
|
"collectWid": taskInfo["wid"],
|
|
"schoolTaskWid": taskInfo["schoolTaskWid"],
|
|
"uaIsCpadaily": gvar.New(true),
|
|
"instanceWid": taskInfo["instanceWid"],
|
|
"longitude": gvar.New(""),
|
|
"latitude": gvar.New(""),
|
|
"address": gvar.New(""),
|
|
}
|
|
|
|
// 获取经纬度、地址
|
|
for _, v := range paramList {
|
|
switch v.ColName {
|
|
case "lon":
|
|
resultForm["longitude"] = gvar.New(v.Content)
|
|
case "lat":
|
|
resultForm["latitude"] = gvar.New(v.Content)
|
|
case "address":
|
|
resultForm["address"] = gvar.New(v.Content)
|
|
}
|
|
}
|
|
|
|
return resultForm
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) Submit(ctx context.Context, form map[string]*gvar.Var, username, deviceId string) {
|
|
cpdailyInfo := cpdaily.GetInfo(ctx)
|
|
|
|
extensionForm := map[string]*gvar.Var{
|
|
"lon": form["longitude"],
|
|
"lat": form["latitude"],
|
|
"model": gvar.New(uuid.New().String()),
|
|
"appVersion": gvar.New(cpdailyInfo.AppVersion),
|
|
"systemVersion": gvar.New(cpdailyInfo.SystemVersion),
|
|
"userId": gvar.New(username),
|
|
"systemName": gvar.New("android"),
|
|
"deviceId": gvar.New(deviceId),
|
|
"cpdailyVersion": gvar.New(cpdailyInfo.AppVersion),
|
|
}
|
|
|
|
extensionString := taskhelper.GetCpdailyExtension(ctx, extensionForm)
|
|
bodyString := taskhelper.GetBodyString(ctx, form)
|
|
|
|
// 构造提交数据
|
|
submitData := map[string]*gvar.Var{
|
|
"lon": form["longitude"],
|
|
"version": gvar.New(cryptoutil.EncryptVersion),
|
|
"calVersion": gvar.New(cpdailyInfo.CalVersion),
|
|
"deviceId": extensionForm["deviceId"],
|
|
"userId": gvar.New(username),
|
|
"systemName": gvar.New("android"),
|
|
"bodyString": gvar.New(bodyString),
|
|
"lat": form["latitude"],
|
|
"systemVersion": gvar.New(cpdailyInfo.SystemVersion),
|
|
"appVersion": gvar.New(cpdailyInfo.AppVersion),
|
|
"model": extensionForm["model"],
|
|
}
|
|
|
|
sign := taskhelper.GetSubmitSign(submitData)
|
|
submitData["sign"] = gvar.New(sign)
|
|
|
|
response, e := c.client.R().
|
|
SetBody(submitData).
|
|
WithContext(ctx).
|
|
SetHeader("Cpdaily-Extension", extensionString).
|
|
Post(c.getReqUrl("submitForm"))
|
|
|
|
utils.OkOrPanic(e, func(err error) error {
|
|
return oops.
|
|
With("host", c.host).
|
|
With("username", username).
|
|
With("deviceId", deviceId).
|
|
In("CpdailyCollectionTask.Submit").
|
|
Wrap(err)
|
|
})
|
|
|
|
responseJSON := utils.ReadGjsonResult(response.Body)
|
|
|
|
if responseJSON.Get("code").String() != "0" {
|
|
message := responseJSON.Get("message").String()
|
|
if message == "" {
|
|
message = "今日校园返回未知错误"
|
|
}
|
|
utils.OkOrPanic(errors.NewBusinessError(503, message), oops.WithContext(ctx).Wrap)
|
|
}
|
|
}
|
|
|
|
// 辅助方法
|
|
func (c *CpdailyCollectionTask) findMatchTask(rows []any, titleGlob string) map[string]*gvar.Var {
|
|
for _, row := range rows {
|
|
rowObj := row.(map[string]*gvar.Var)
|
|
title := rowObj["subject"].String()
|
|
if match.Match(title, titleGlob) {
|
|
return rowObj
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) isTextType(fieldType string) bool {
|
|
textTypes := []string{"1", "5", "6", "7", "11", "12"}
|
|
return c.contains(textTypes, fieldType)
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) isPicType(fieldType string) bool {
|
|
picTypes := []string{"4", "16", "24", "25"}
|
|
return c.contains(picTypes, fieldType)
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) contains(slice []string, item string) bool {
|
|
return slices.Contains(slice, item)
|
|
}
|
|
|
|
func (c *CpdailyCollectionTask) containsUint(slice []uint, item uint) bool {
|
|
return slices.Contains(slice, item)
|
|
}
|